Loading services/core/java/com/android/server/BinaryTransparencyService.java +3 −8 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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 " Loading Loading @@ -1230,10 +1229,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) { Loading @@ -1256,7 +1253,6 @@ public class BinaryTransparencyService extends SystemService { return; } sTimeLastRanMs = System.currentTimeMillis(); sScheduled.set(false); jobFinished(params, false); }).start(); Loading @@ -1277,7 +1273,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; } Loading @@ -1303,7 +1299,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)); Loading tests/BinaryTransparencyHostTest/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ java_test_host { data: [ ":BinaryTransparencyTestApp", ":EasterEgg", ":com.android.apex.cts.shim.v2_rebootless_prebuilt", ], test_suites: [ "general-tests", Loading tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +74 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -63,6 +72,28 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { runDeviceTest("testCollectAllUpdatedPreloadInfo"); } @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 Loading @@ -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); } } } Loading
services/core/java/com/android/server/BinaryTransparencyService.java +3 −8 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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 " Loading Loading @@ -1230,10 +1229,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) { Loading @@ -1256,7 +1253,6 @@ public class BinaryTransparencyService extends SystemService { return; } sTimeLastRanMs = System.currentTimeMillis(); sScheduled.set(false); jobFinished(params, false); }).start(); Loading @@ -1277,7 +1273,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; } Loading @@ -1303,7 +1299,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)); Loading
tests/BinaryTransparencyHostTest/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ java_test_host { data: [ ":BinaryTransparencyTestApp", ":EasterEgg", ":com.android.apex.cts.shim.v2_rebootless_prebuilt", ], test_suites: [ "general-tests", Loading
tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java +74 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -63,6 +72,28 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test { runDeviceTest("testCollectAllUpdatedPreloadInfo"); } @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 Loading @@ -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); } } }