Allow observers to be loaded correctly

Removes dependency on PackageManager when loading observers early
in the boot. This is done by associating each MonitoredPackage with
a "packageName" string rather than a VersionedPackage. Incoming
failure calls will be matched against this string.

Added test case to cover this condition.

Test: Manual. Install apk with rollback enabled,
      reboot, adb shell dumpsys rollback,
      ensure package is still being monitored
Test: atest StagedRollbackTest
Bug: 177793481
Change-Id: I282ac75a9e97a58c3c0bf3e8978fd010ec5294cd
This commit is contained in:
Gavin Corkery
2021-02-15 14:00:50 +00:00
parent c726ac66ba
commit f72f94ee54
3 changed files with 75 additions and 13 deletions

View File

@@ -902,10 +902,13 @@ public class PackageWatchdog {
if (registeredObserver != null) {
Iterator<MonitoredPackage> it = failedPackages.iterator();
while (it.hasNext()) {
VersionedPackage versionedPkg = it.next().mPackage;
Slog.i(TAG, "Explicit health check failed for package " + versionedPkg);
registeredObserver.execute(versionedPkg,
PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
VersionedPackage versionedPkg = getVersionedPackage(it.next().getName());
if (versionedPkg != null) {
Slog.i(TAG,
"Explicit health check failed for package " + versionedPkg);
registeredObserver.execute(versionedPkg,
PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
}
}
}
}
@@ -1342,11 +1345,7 @@ public class PackageWatchdog {
MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) {
VersionedPackage pkg = getVersionedPackage(name);
if (pkg == null) {
return null;
}
return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs,
return new MonitoredPackage(name, durationMs, healthCheckDurationMs,
hasPassedHealthCheck, mitigationCalls);
}
@@ -1371,7 +1370,7 @@ public class PackageWatchdog {
* instances of this class.
*/
class MonitoredPackage {
private final VersionedPackage mPackage;
private final String mPackageName;
// Times when package failures happen sorted in ascending order
@GuardedBy("mLock")
private final LongArrayQueue mFailureHistory = new LongArrayQueue();
@@ -1399,10 +1398,10 @@ public class PackageWatchdog {
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
MonitoredPackage(VersionedPackage pkg, long durationMs,
MonitoredPackage(String packageName, long durationMs,
long healthCheckDurationMs, boolean hasPassedHealthCheck,
LongArrayQueue mitigationCalls) {
mPackage = pkg;
mPackageName = packageName;
mDurationMs = durationMs;
mHealthCheckDurationMs = healthCheckDurationMs;
mHasPassedHealthCheck = hasPassedHealthCheck;
@@ -1556,7 +1555,7 @@ public class PackageWatchdog {
/** Returns the monitored package name. */
private String getName() {
return mPackage.getPackageName();
return mPackageName;
}
/**

View File

@@ -495,6 +495,45 @@ public class StagedRollbackTest {
assertThat(sm.isCheckpointSupported()).isTrue();
}
@Test
public void testWatchdogMonitorsAcrossReboots_Phase1_Install() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
Install.single(TestApp.ACrashing2).setEnableRollback().setStaged().commit();
}
@Test
public void testWatchdogMonitorsAcrossReboots_Phase2_VerifyInstall() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
// Trigger rollback of test app.
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
Integer.toString(5), false);
// The final crash that causes rollback will come from the host side.
RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
}
@Test
public void testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback() {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
RollbackManager rm = RollbackUtils.getRollbackManager();
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
rm.getRecentlyCommittedRollbacks(), TestApp.A);
assertThat(rollback).isNotNull();
assertThat(rollback).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
assertThat(rollback).causePackagesContainsExactly(TestApp.ACrashing2);
assertThat(rollback).isStaged();
assertThat(rollback.getCommittedSessionId()).isNotEqualTo(-1);
}
@Test
public void hasMainlineModule() throws Exception {
String pkgName = getModuleMetadataPackageName();

View File

@@ -554,6 +554,30 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
});
}
/**
* Tests that packages are monitored across multiple reboots.
*/
@Test
public void testWatchdogMonitorsAcrossReboots() throws Exception {
runPhase("testWatchdogMonitorsAcrossReboots_Phase1_Install");
// The first reboot will make the rollback available.
// Information about which packages are monitored will be persisted to a file before the
// second reboot, and read from disk after the second reboot.
getDevice().reboot();
getDevice().reboot();
runPhase("testWatchdogMonitorsAcrossReboots_Phase2_VerifyInstall");
// Launch the app to crash to trigger rollback
startActivity(TESTAPP_A);
// Wait for reboot to happen
waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
getDevice().waitForDeviceAvailable();
runPhase("testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback");
}
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";