diff options
author | 2023-01-24 15:40:13 -0800 | |
---|---|---|
committer | 2023-01-26 12:07:40 -0800 | |
commit | 3e6ee94698cc4434799e5cbe89b7289a3af47e99 (patch) | |
tree | 1a75454406f60f50df22e1334a429b3d536cb80c | |
parent | a97f246bdbebb9fb823501937308b0f2bbf285ac (diff) |
Add tests to verify job scheduling on events
... of rebootless APEX and preload update installation.
Changes made to the actual service:
* DO_BINARY_MEASUREMENTS_JOB_ID is now a constant, like most of jobs in
the framework. This makes it easy to use from the host.
* Replace the local state of sScheduled with a query to JobScheduler,
which is the source of truth. I've messed up the state during test
and manual interaction, and this is supposed to make it more
resilient.
Bug: 265244016
Test: atest BinaryTransparencyHostTest
Test: atest BinaryTransparencyServiceTest
Change-Id: I17872bbded0b5c5c44e6fbd1c669f1eb00f3333e
3 files changed, 78 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 6fdbc0df7c2f..b9282763648f 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -96,7 +96,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; /** @@ -287,7 +286,7 @@ public class BinaryTransparencyService extends SystemService { * - dynamically installed mobile bundled apps (MBAs) (new in Android U) */ public void recordMeasurementsForAllPackages() { - // check if we should record the resulting measurements + // check if we should measure and record long currentTimeMs = System.currentTimeMillis(); if ((currentTimeMs - mMeasurementsLastRecordedMs) < RECORD_MEASUREMENTS_COOLDOWN_MS) { Slog.d(TAG, "Skip measurement since the last measurement was only taken at " @@ -1227,10 +1226,8 @@ public class BinaryTransparencyService extends SystemService { * JobService to measure all covered binaries and record result to Westworld. */ public static class UpdateMeasurementsJobService extends JobService { - private static AtomicBoolean sScheduled = new AtomicBoolean(); private static long sTimeLastRanMs = 0; - private static final int DO_BINARY_MEASUREMENTS_JOB_ID = - UpdateMeasurementsJobService.class.hashCode(); + private static final int DO_BINARY_MEASUREMENTS_JOB_ID = 1740526926; @Override public boolean onStartJob(JobParameters params) { @@ -1253,7 +1250,6 @@ public class BinaryTransparencyService extends SystemService { return; } sTimeLastRanMs = System.currentTimeMillis(); - sScheduled.set(false); jobFinished(params, false); }).start(); @@ -1274,7 +1270,7 @@ public class BinaryTransparencyService extends SystemService { return; } - if (sScheduled.get()) { + if (jobScheduler.getPendingJob(DO_BINARY_MEASUREMENTS_JOB_ID) != null) { Slog.d(TAG, "A measurement job has already been scheduled."); return; } @@ -1300,7 +1296,6 @@ public class BinaryTransparencyService extends SystemService { Slog.e(TAG, "Failed to schedule job to measure binaries."); return; } - sScheduled.set(true); Slog.d(TAG, TextUtils.formatSimple( "Job %d to measure binaries was scheduled successfully.", DO_BINARY_MEASUREMENTS_JOB_ID)); diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index 142e3ddf9fe7..dc6bdff6716c 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -35,6 +35,7 @@ java_test_host { data: [ ":BinaryTransparencyTestApp", ":EasterEgg", + ":com.android.apex.cts.shim.v2_rebootless_prebuilt", ], test_suites: [ "general-tests", diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java index 84bed92f5117..8db3d003d146 100644 --- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java @@ -17,6 +17,7 @@ package android.transparency.test; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,14 +30,22 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.TimeUnit; + // TODO: Add @Presubmit @RunWith(DeviceJUnit4ClassRunner.class) public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { private static final String PACKAGE_NAME = "android.transparency.test.app"; + private static final String JOB_ID = "1740526926"; + + /** Waiting time for the job to be scheduled */ + private static final int JOB_CREATION_MAX_SECONDS = 5; + @After public void tearDown() throws Exception { uninstallPackage("com.android.egg"); + uninstallRebootlessApex(); } @Test @@ -64,6 +73,28 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { } @Test + public void testRebootlessApexUpdateTriggersJobScheduling() throws Exception { + cancelPendingJob(); + installRebootlessApex(); + + // Verify + expectJobToBeScheduled(); + // Just cancel since we can't verifying very meaningfully. + cancelPendingJob(); + } + + @Test + public void testPreloadUpdateTriggersJobScheduling() throws Exception { + cancelPendingJob(); + installPackage("EasterEgg.apk"); + + // Verify + expectJobToBeScheduled(); + // Just cancel since we can't verifying very meaningfully. + cancelPendingJob(); + } + + @Test public void testMeasureMbas() throws Exception { // TODO(265244016): figure out a way to install an MBA } @@ -74,4 +105,47 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { options.setTestMethodName(method); runDeviceTests(options); } + + private void cancelPendingJob() throws DeviceNotAvailableException { + CommandResult result = getDevice().executeShellV2Command( + "cmd jobscheduler cancel android " + JOB_ID); + assertTrue(result.getStatus() == CommandStatus.SUCCESS); + } + + private void expectJobToBeScheduled() throws Exception { + for (int i = 0; i < JOB_CREATION_MAX_SECONDS; i++) { + CommandResult result = getDevice().executeShellV2Command( + "cmd jobscheduler get-job-state android " + JOB_ID); + String state = result.getStdout().toString(); + if (state.startsWith("unknown")) { + // The job hasn't been scheduled yet. So try again. + TimeUnit.SECONDS.sleep(1); + } else if (result.getExitCode() != 0) { + fail("Failing due to unexpected job state: " + result); + } else { + // The job exists, which is all we care about here + return; + } + } + fail("Timed out waiting for the job to be scheduled"); + } + + private void installRebootlessApex() throws Exception { + installPackage("com.android.apex.cts.shim.v2_rebootless.apex", "--force-non-staged"); + } + + private void uninstallRebootlessApex() throws DeviceNotAvailableException { + // Reboot only if the APEX is not the pre-install one. + CommandResult result = getDevice().executeShellV2Command( + "pm list packages -f --apex-only |grep com.android.apex.cts.shim"); + assertTrue(result.getStatus() == CommandStatus.SUCCESS); + if (result.getStdout().contains("/data/apex/active/")) { + uninstallPackage("com.android.apex.cts.shim"); + getDevice().reboot(); + + // Reboot enforces SELinux. Make it permissive again. + CommandResult runResult = getDevice().executeShellV2Command("setenforce 0"); + assertTrue(runResult.getStatus() == CommandStatus.SUCCESS); + } + } } |