Merge "Write out the BCC when signing"
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index b332543..31f8458 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -16,6 +16,8 @@
package com.android.virt.fs;
+import static android.virt.test.LogArchiver.archiveLogThenDelete;
+
import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
@@ -26,11 +28,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.RootPermissionTest;
import android.virt.test.CommandRunner;
-import android.virt.test.VirtualizationTestCaseBase;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.util.PollingCheck;
@@ -42,14 +43,13 @@
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import org.junit.After;
-import org.junit.AssumptionViolatedException;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -63,8 +63,7 @@
@RootPermissionTest
@RunWith(DeviceJUnit4ClassRunner.class)
-@Ignore("TODO(b/229823049): Make this work")
-public final class AuthFsHostTest extends VirtualizationTestCaseBase {
+public final class AuthFsHostTest extends BaseHostJUnit4Test {
/** Test directory on Android where data are located */
private static final String TEST_DIR = "/data/local/tmp/authfs";
@@ -72,6 +71,9 @@
/** Output directory where the test can generate output on Android */
private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
+ /** VM's log file */
+ private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
+
/** File name of the test APK */
private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
@@ -124,20 +126,18 @@
@BeforeClassWithInfo
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
assertNotNull(testInfo.getDevice());
- ITestDevice androidDevice = testInfo.getDevice();
+ if (!(testInfo.getDevice() instanceof TestDevice)) {
+ CLog.w("Unexpected type of ITestDevice. Skipping.");
+ return;
+ }
+ TestDevice androidDevice = (TestDevice) testInfo.getDevice();
sAndroid = new CommandRunner(androidDevice);
- try {
- testIfDeviceIsCapable(androidDevice);
- } catch (AssumptionViolatedException e) {
- // NB: The assumption exception is NOT handled by the test infra when it is thrown from
- // a class method (see b/37502066). This has not only caused the loss of log, but also
- // prevented the test cases to be reported at all and thus confused the test infra.
- //
- // Since we want to avoid the big overhead to start the VM repeatedly on CF, let's catch
- // AssumptionViolatedException and emulate it artifitially.
- CLog.e("Assumption failed: " + e);
- sAssumptionFailed = true;
+ // NB: We can't use assumeTrue because the assumption exception is NOT handled by the test
+ // infra when it is thrown from a class method (see b/37502066). We need to skip both here
+ // and in setUp.
+ if (!androidDevice.supportsMicrodroid()) {
+ CLog.i("Microdroid not supported. Skipping.");
return;
}
@@ -150,11 +150,12 @@
.addExtraIdsigPath(EXTRA_IDSIG_PATH)
.build((TestDevice) androidDevice);
+ // From this point on, we need to tear down the Microdroid instance
+ sMicrodroid = new CommandRunner(microdroidDevice);
+
// Root because authfs (started from shell in this test) currently require root to open
// /dev/fuse and mount the FUSE.
assertThat(microdroidDevice.enableAdbRoot()).isTrue();
-
- sMicrodroid = new CommandRunner(microdroidDevice);
}
@AfterClassWithInfo
@@ -173,7 +174,7 @@
@Before
public void setUp() throws Exception {
- assumeFalse(sAssumptionFailed);
+ assumeTrue(((TestDevice) getDevice()).supportsMicrodroid());
sAndroid.run("mkdir " + TEST_OUTPUT_DIR);
}
@@ -210,11 +211,11 @@
+ VMADDR_CID_HOST);
// Action
- String actualHashUnverified4m = computeFileHashOnMicrodroid(MOUNT_DIR + "/6");
- String actualHash4m = computeFileHashOnMicrodroid(MOUNT_DIR + "/3");
+ String actualHashUnverified4m = computeFileHash(sMicrodroid, MOUNT_DIR + "/6");
+ String actualHash4m = computeFileHash(sMicrodroid, MOUNT_DIR + "/3");
// Verify
- String expectedHash4m = computeFileHashOnAndroid(TEST_DIR + "/input.4m");
+ String expectedHash4m = computeFileHash(sAndroid, TEST_DIR + "/input.4m");
assertEquals("Inconsistent hash from /authfs/6: ", expectedHash4m, actualHashUnverified4m);
assertEquals("Inconsistent hash from /authfs/3: ", expectedHash4m, actualHash4m);
@@ -234,12 +235,12 @@
+ VMADDR_CID_HOST);
// Action
- String actualHash4k = computeFileHashOnMicrodroid(MOUNT_DIR + "/3");
- String actualHash4k1 = computeFileHashOnMicrodroid(MOUNT_DIR + "/6");
+ String actualHash4k = computeFileHash(sMicrodroid, MOUNT_DIR + "/3");
+ String actualHash4k1 = computeFileHash(sMicrodroid, MOUNT_DIR + "/6");
// Verify
- String expectedHash4k = computeFileHashOnAndroid(TEST_DIR + "/input.4k");
- String expectedHash4k1 = computeFileHashOnAndroid(TEST_DIR + "/input.4k1");
+ String expectedHash4k = computeFileHash(sAndroid, TEST_DIR + "/input.4k");
+ String expectedHash4k1 = computeFileHash(sAndroid, TEST_DIR + "/input.4k1");
assertEquals("Inconsistent hash from /authfs/3: ", expectedHash4k, actualHash4k);
assertEquals("Inconsistent hash from /authfs/6: ", expectedHash4k1, actualHash4k1);
@@ -254,7 +255,7 @@
runAuthFsOnMicrodroid("--remote-ro-file 3:" + DIGEST_4M + " --cid " + VMADDR_CID_HOST);
// Verify
- assertFalse(copyFileOnMicrodroid(MOUNT_DIR + "/3", "/dev/null"));
+ assertFalse(copyFile(sMicrodroid, MOUNT_DIR + "/3", "/dev/null"));
}
@Test
@@ -267,10 +268,10 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFileOnMicrodroid(srcPath, destPath));
+ assertTrue(copyFile(sMicrodroid, srcPath, destPath));
// Verify
- String expectedHash = computeFileHashOnMicrodroid(srcPath);
+ String expectedHash = computeFileHash(sMicrodroid, srcPath);
expectBackingFileConsistency(destPath, backendPath, expectedHash);
}
@@ -283,30 +284,32 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFileOnMicrodroid(srcPath, destPath));
+ assertTrue(copyFile(sMicrodroid, srcPath, destPath));
// Action
// Tampering with the first 2 4K-blocks of the backing file.
- zeroizeFileOnAndroid(backendPath, /* size */ 8192, /* offset */ 0);
+ assertTrue(
+ writeZerosAtFileOffset(sAndroid, backendPath,
+ /* offset */ 0, /* number */ 8192, /* writeThrough */ false));
// Verify
// Write to a block partially requires a read back to calculate the new hash. It should fail
// when the content is inconsistent to the known hash. Use direct I/O to avoid simply
// writing to the filesystem cache.
assertFalse(
- writeZerosAtFileOffsetOnMicrodroid(
- destPath, /* offset */ 0, /* number */ 1024, /* writeThrough */ true));
+ writeZerosAtFileOffset(sMicrodroid, destPath,
+ /* offset */ 0, /* number */ 1024, /* writeThrough */ true));
// A full 4K write does not require to read back, so write can succeed even if the backing
// block has already been tampered.
assertTrue(
- writeZerosAtFileOffsetOnMicrodroid(
- destPath, /* offset */ 4096, /* number */ 4096, /* writeThrough */ false));
+ writeZerosAtFileOffset(sMicrodroid, destPath,
+ /* offset */ 4096, /* number */ 4096, /* writeThrough */ false));
// Otherwise, a partial write with correct backing file should still succeed.
assertTrue(
- writeZerosAtFileOffsetOnMicrodroid(
- destPath, /* offset */ 8192, /* number */ 1024, /* writeThrough */ false));
+ writeZerosAtFileOffset(sMicrodroid, destPath,
+ /* offset */ 8192, /* number */ 1024, /* writeThrough */ false));
}
@Test
@@ -318,20 +321,20 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFileOnMicrodroid(srcPath, destPath));
+ assertTrue(copyFile(sMicrodroid, srcPath, destPath));
// Action
// Tampering with the first 4K-block of the backing file.
- zeroizeFileOnAndroid(backendPath, /* size */ 4096, /* offset */ 0);
+ assertTrue(
+ writeZerosAtFileOffset(sAndroid, backendPath,
+ /* offset */ 0, /* number */ 4096, /* writeThrough */ false));
// Verify
// Force dropping the page cache, so that the next read can be validated.
sMicrodroid.run("echo 1 > /proc/sys/vm/drop_caches");
// A read will fail if the backing data has been tampered.
- assertFalse(checkReadAtFileOffsetOnMicrodroid(
- destPath, /* offset */ 0, /* number */ 4096));
- assertTrue(checkReadAtFileOffsetOnMicrodroid(
- destPath, /* offset */ 4096, /* number */ 4096));
+ assertFalse(checkReadAt(sMicrodroid, destPath, /* offset */ 0, /* number */ 4096));
+ assertTrue(checkReadAt(sMicrodroid, destPath, /* offset */ 4096, /* number */ 4096));
}
@Test
@@ -342,17 +345,19 @@
String outputPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- createFileWithOnesOnMicrodroid(outputPath, 8192);
+ createFileWithOnes(sMicrodroid, outputPath, 8192);
// Action
// Tampering with the last 4K-block of the backing file.
- zeroizeFileOnAndroid(backendPath, /* size */ 1, /* offset */ 4096);
+ assertTrue(
+ writeZerosAtFileOffset(sAndroid, backendPath,
+ /* offset */ 4096, /* number */ 1, /* writeThrough */ false));
// Verify
// A resize (to a non-multiple of 4K) will fail if the last backing chunk has been
// tampered. The original data is necessary (and has to be verified) to calculate the new
// hash with shorter data.
- assertFalse(resizeFileOnMicrodroid(outputPath, 8000));
+ assertFalse(resizeFile(sMicrodroid, outputPath, 8000));
}
@Test
@@ -364,22 +369,22 @@
String backendPath = TEST_OUTPUT_DIR + "/out.file";
// Action & Verify
- createFileWithOnesOnMicrodroid(outputPath, 10000);
- assertEquals(getFileSizeInBytesOnMicrodroid(outputPath), 10000);
+ createFileWithOnes(sMicrodroid, outputPath, 10000);
+ assertEquals(getFileSizeInBytes(sMicrodroid, outputPath), 10000);
expectBackingFileConsistency(
outputPath,
backendPath,
"684ad25fdc2bbb80cbc910dd1bde6d5499ccf860ca6ee44704b77ec445271353");
- assertTrue(resizeFileOnMicrodroid(outputPath, 15000));
- assertEquals(getFileSizeInBytesOnMicrodroid(outputPath), 15000);
+ assertTrue(resizeFile(sMicrodroid, outputPath, 15000));
+ assertEquals(getFileSizeInBytes(sMicrodroid, outputPath), 15000);
expectBackingFileConsistency(
outputPath,
backendPath,
"567c89f62586e0d33369157afdfe99a2fa36cdffb01e91dcdc0b7355262d610d");
- assertTrue(resizeFileOnMicrodroid(outputPath, 5000));
- assertEquals(getFileSizeInBytesOnMicrodroid(outputPath), 5000);
+ assertTrue(resizeFile(sMicrodroid, outputPath, 5000));
+ assertEquals(getFileSizeInBytes(sMicrodroid, outputPath), 5000);
expectBackingFileConsistency(
outputPath,
backendPath,
@@ -399,16 +404,16 @@
// Can create a new file to write.
String expectedAndroidPath = androidOutputDir + "/file";
String authfsPath = authfsOutputDir + "/file";
- createFileWithOnesOnMicrodroid(authfsPath, 10000);
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsPath), 10000);
+ createFileWithOnes(sMicrodroid, authfsPath, 10000);
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsPath), 10000);
expectBackingFileConsistency(
authfsPath,
expectedAndroidPath,
"684ad25fdc2bbb80cbc910dd1bde6d5499ccf860ca6ee44704b77ec445271353");
// Regular file operations work, e.g. resize.
- assertTrue(resizeFileOnMicrodroid(authfsPath, 15000));
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsPath), 15000);
+ assertTrue(resizeFile(sMicrodroid, authfsPath, 15000));
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsPath), 15000);
expectBackingFileConsistency(
authfsPath,
expectedAndroidPath,
@@ -428,21 +433,21 @@
// Can create nested directories and can create a file in one.
sMicrodroid.run("mkdir " + authfsOutputDir + "/new_dir");
sMicrodroid.run("mkdir -p " + authfsOutputDir + "/we/need/to/go/deeper");
- createFileWithOnesOnMicrodroid(authfsOutputDir + "/new_dir/file1", 10000);
- createFileWithOnesOnMicrodroid(authfsOutputDir + "/we/need/file2", 10000);
+ createFileWithOnes(sMicrodroid, authfsOutputDir + "/new_dir/file1", 10000);
+ createFileWithOnes(sMicrodroid, authfsOutputDir + "/we/need/file2", 10000);
// Verify
// Directories show up in Android.
sAndroid.run("test -d " + androidOutputDir + "/new_dir");
sAndroid.run("test -d " + androidOutputDir + "/we/need/to/go/deeper");
// Files exist in Android. Hashes on Microdroid and Android are consistent.
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsOutputDir + "/new_dir/file1"), 10000);
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsOutputDir + "/new_dir/file1"), 10000);
expectBackingFileConsistency(
authfsOutputDir + "/new_dir/file1",
androidOutputDir + "/new_dir/file1",
"684ad25fdc2bbb80cbc910dd1bde6d5499ccf860ca6ee44704b77ec445271353");
// Same to file in a nested directory.
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsOutputDir + "/we/need/file2"), 10000);
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsOutputDir + "/we/need/file2"), 10000);
expectBackingFileConsistency(
authfsOutputDir + "/we/need/file2",
androidOutputDir + "/we/need/file2",
@@ -460,10 +465,10 @@
// Action & Verify
sMicrodroid.run("echo -n foo > " + authfsOutputDir + "/file");
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsOutputDir + "/file"), 3);
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsOutputDir + "/file"), 3);
// Can override a file and write normally.
- createFileWithOnesOnMicrodroid(authfsOutputDir + "/file", 10000);
- assertEquals(getFileSizeInBytesOnMicrodroid(authfsOutputDir + "/file"), 10000);
+ createFileWithOnes(sMicrodroid, authfsOutputDir + "/file", 10000);
+ assertEquals(getFileSizeInBytes(sMicrodroid, authfsOutputDir + "/file"), 10000);
expectBackingFileConsistency(
authfsOutputDir + "/file",
androidOutputDir + "/file",
@@ -507,7 +512,7 @@
sMicrodroid.run("test ! -d " + authfsOutputDir + "/dir/dir2");
sAndroid.run("test ! -d " + androidOutputDir + "/dir/dir2");
// Can only delete a directory if empty
- assertFailedOnMicrodroid("rmdir " + authfsOutputDir + "/dir");
+ assertFailed(sMicrodroid, "rmdir " + authfsOutputDir + "/dir");
sMicrodroid.run("test -d " + authfsOutputDir + "/dir"); // still there
sMicrodroid.run("rm " + authfsOutputDir + "/dir/file");
sMicrodroid.run("rmdir " + authfsOutputDir + "/dir");
@@ -531,10 +536,10 @@
// Action & Verify
// Cannot create directory if an entry with the same name already exists.
- assertFailedOnMicrodroid("mkdir " + authfsOutputDir + "/some_file");
- assertFailedOnMicrodroid("mkdir " + authfsOutputDir + "/some_dir");
- assertFailedOnMicrodroid("mkdir " + authfsOutputDir + "/some_dir/file");
- assertFailedOnMicrodroid("mkdir " + authfsOutputDir + "/some_dir/dir");
+ assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_file");
+ assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir");
+ assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir/file");
+ assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir/dir");
}
@Test
@@ -581,10 +586,10 @@
// Action
String actualHash =
- computeFileHashOnMicrodroid(authfsInputDir + "/system/framework/framework.jar");
+ computeFileHash(sMicrodroid, authfsInputDir + "/system/framework/framework.jar");
// Verify
- String expectedHash = computeFileHashOnAndroid("/system/framework/framework.jar");
+ String expectedHash = computeFileHash(sAndroid, "/system/framework/framework.jar");
assertEquals("Expect consistent hash through /authfs/3: ", expectedHash, actualHash);
}
@@ -598,7 +603,7 @@
// Verify
sMicrodroid.run("test -f " + authfsInputDir + "/system/framework/services.jar");
- assertFailedOnMicrodroid("test -f " + authfsInputDir + "/system/bin/sh");
+ assertFailed(sMicrodroid, "test -f " + authfsInputDir + "/system/bin/sh");
}
@Test
@@ -653,8 +658,8 @@
sMicrodroid.run("chmod 321 " + MOUNT_DIR + "/3");
expectFileMode("--wx-w---x", MOUNT_DIR + "/3", TEST_OUTPUT_DIR + "/file");
// Can't set the disallowed bits
- assertFailedOnMicrodroid("chmod +s " + MOUNT_DIR + "/3");
- assertFailedOnMicrodroid("chmod +t " + MOUNT_DIR + "/3");
+ assertFailed(sMicrodroid, "chmod +s " + MOUNT_DIR + "/3");
+ assertFailed(sMicrodroid, "chmod +t " + MOUNT_DIR + "/3");
}
@Test
@@ -676,8 +681,8 @@
sMicrodroid.run("chmod 321 " + authfsOutputDir + "/dir");
expectFileMode("d-wx-w---x", authfsOutputDir + "/dir", TEST_OUTPUT_DIR + "/dir");
// Can't set the disallowed bits
- assertFailedOnMicrodroid("chmod +s " + authfsOutputDir + "/dir/dir2");
- assertFailedOnMicrodroid("chmod +t " + authfsOutputDir + "/dir");
+ assertFailed(sMicrodroid, "chmod +s " + authfsOutputDir + "/dir/dir2");
+ assertFailed(sMicrodroid, "chmod +t " + authfsOutputDir + "/dir");
}
@Test
@@ -699,8 +704,8 @@
sMicrodroid.run("chmod 321 " + authfsOutputDir + "/file2");
expectFileMode("--wx-w---x", authfsOutputDir + "/file2", TEST_OUTPUT_DIR + "/file2");
// Can't set the disallowed bits
- assertFailedOnMicrodroid("chmod +s " + authfsOutputDir + "/file");
- assertFailedOnMicrodroid("chmod +t " + authfsOutputDir + "/file2");
+ assertFailed(sMicrodroid, "chmod +s " + authfsOutputDir + "/file");
+ assertFailed(sMicrodroid, "chmod +t " + authfsOutputDir + "/file2");
}
@Test
@@ -727,35 +732,17 @@
private void expectBackingFileConsistency(
String authFsPath, String backendPath, String expectedHash)
throws DeviceNotAvailableException {
- String hashOnAuthFs = computeFileHashOnMicrodroid(authFsPath);
+ String hashOnAuthFs = computeFileHash(sMicrodroid, authFsPath);
assertEquals("File hash is different to expectation", expectedHash, hashOnAuthFs);
- String hashOfBackingFile = computeFileHashOnAndroid(backendPath);
+ String hashOfBackingFile = computeFileHash(sAndroid, backendPath);
assertEquals(
"Inconsistent file hash on the backend storage", hashOnAuthFs, hashOfBackingFile);
}
- private String computeFileHashOnMicrodroid(String path) throws DeviceNotAvailableException {
- String result = sMicrodroid.run("sha256sum " + path);
- String[] tokens = result.split("\\s");
- if (tokens.length > 0) {
- return tokens[0];
- } else {
- CLog.e("Unrecognized output by sha256sum: " + result);
- return "";
- }
- }
-
- private boolean copyFileOnMicrodroid(String src, String dest)
+ private static String computeFileHash(CommandRunner runner, String path)
throws DeviceNotAvailableException {
- // TODO(b/182576497): cp returns error because close(2) returns ENOSYS in the current authfs
- // implementation. We should probably fix that since programs can expect close(2) return 0.
- String cmd = "cat " + src + " > " + dest;
- return sMicrodroid.tryRun(cmd) != null;
- }
-
- private String computeFileHashOnAndroid(String path) throws DeviceNotAvailableException {
- String result = sAndroid.run("sha256sum " + path);
+ String result = runner.run("sha256sum " + path);
String[] tokens = result.split("\\s");
if (tokens.length > 0) {
return tokens[0];
@@ -765,6 +752,15 @@
}
}
+ private static boolean copyFile(CommandRunner runner, String src, String dest)
+ throws DeviceNotAvailableException {
+ // toybox's cp(1) implementation ignores most read(2) errors, and it's unclear what the
+ // canonical behavior should be (not mentioned in manpage). For this test, use cat(1) in
+ // order to fail on I/O error.
+ CommandResult result = runner.runForResult("cat " + src + " > " + dest);
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
private void expectFileMode(String expected, String microdroidPath, String androidPath)
throws DeviceNotAvailableException {
String actual = sMicrodroid.run("stat -c '%A' " + microdroidPath);
@@ -774,34 +770,35 @@
assertEquals("Inconsistent mode for " + androidPath + " (android)", expected, actual);
}
- private boolean resizeFileOnMicrodroid(String path, long size)
+ private static boolean resizeFile(CommandRunner runner, String path, long size)
throws DeviceNotAvailableException {
- CommandResult result = sMicrodroid.runForResult("truncate -c -s " + size + " " + path);
+ CommandResult result = runner.runForResult("truncate -c -s " + size + " " + path);
return result.getStatus() == CommandStatus.SUCCESS;
}
- private long getFileSizeInBytesOnMicrodroid(String path) throws DeviceNotAvailableException {
- return Long.parseLong(sMicrodroid.run("stat -c '%s' " + path));
+ private static long getFileSizeInBytes(CommandRunner runner, String path)
+ throws DeviceNotAvailableException {
+ return Long.parseLong(runner.run("stat -c '%s' " + path));
}
- private void createFileWithOnesOnMicrodroid(String filePath, long numberOfOnes)
+ private static void createFileWithOnes(CommandRunner runner, String filePath, long numberOfOnes)
throws DeviceNotAvailableException {
- sMicrodroid.run(
+ runner.run(
"yes $'\\x01' | tr -d '\\n' | dd bs=1 count=" + numberOfOnes + " of=" + filePath);
}
- private boolean checkReadAtFileOffsetOnMicrodroid(String filePath, long offset, long size)
- throws DeviceNotAvailableException {
+ private static boolean checkReadAt(CommandRunner runner, String filePath, long offset,
+ long size) throws DeviceNotAvailableException {
String cmd = "dd if=" + filePath + " of=/dev/null bs=1 count=" + size;
if (offset > 0) {
cmd += " skip=" + offset;
}
- CommandResult result = sMicrodroid.runForResult(cmd);
+ CommandResult result = runner.runForResult(cmd);
return result.getStatus() == CommandStatus.SUCCESS;
}
- private boolean writeZerosAtFileOffsetOnMicrodroid(
- String filePath, long offset, long numberOfZeros, boolean writeThrough)
+ private static boolean writeZerosAtFileOffset(CommandRunner runner, String filePath,
+ long offset, long numberOfZeros, boolean writeThrough)
throws DeviceNotAvailableException {
String cmd = "dd if=/dev/zero of=" + filePath + " bs=1 count=" + numberOfZeros
+ " conv=notrunc";
@@ -811,14 +808,14 @@
if (writeThrough) {
cmd += " direct";
}
- CommandResult result = sMicrodroid.runForResult(cmd);
+ CommandResult result = runner.runForResult(cmd);
return result.getStatus() == CommandStatus.SUCCESS;
}
- private void zeroizeFileOnAndroid(String filePath, long size, long offset)
+ private static void assertFailed(CommandRunner runner, String... cmd)
throws DeviceNotAvailableException {
- sAndroid.run("dd if=/dev/zero of=" + filePath + " bs=1 count=" + size + " conv=notrunc"
- + " seek=" + offset);
+ CommandResult result = runner.runForResult(cmd);
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.FAILED);
}
private void runAuthFsOnMicrodroid(String flags) {
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 16dc2cf..839280c 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -64,6 +64,8 @@
/// Comma separated list of host CPUs where vCPUs are assigned to. If None, any host CPU can be
/// used to run any vCPU.
pub cpu_set: Option<String>,
+ /// List of task profiles to apply to the VM
+ pub task_profiles: Vec<String>,
/// If present, overrides the path to the VM config JSON file
pub config_path: Option<String>,
/// If present, overrides the amount of RAM to give the VM
@@ -137,6 +139,7 @@
memoryMib: parameters.memory_mib.unwrap_or(0), // 0 means use the default
numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
cpuAffinity: parameters.cpu_set.clone(),
+ taskProfiles: parameters.task_profiles.clone(),
});
let vm = service
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 587314c..60bf20f 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -98,7 +98,14 @@
}
};
let cpu_set = system_properties::read(DEX2OAT_CPU_SET_PROP_NAME)?;
- Ok(VmParameters { cpus, cpu_set, memory_mib: Some(VM_MEMORY_MIB), ..Default::default() })
+ let task_profiles = vec!["VMCompilationPerformance".to_string()];
+ Ok(VmParameters {
+ cpus,
+ cpu_set,
+ task_profiles,
+ memory_mib: Some(VM_MEMORY_MIB),
+ ..Default::default()
+ })
}
// Ensures we only run one instance at a time.
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 3a2d581..7b5d5ab 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -238,6 +238,9 @@
parcel.memoryMib = mMemoryMib;
parcel.numCpus = mNumCpus;
parcel.cpuAffinity = mCpuAffinity;
+ // Don't allow apps to set task profiles ... at last for now. Also, don't forget to
+ // validate the string because these are appended to the cmdline argument.
+ parcel.taskProfiles = new String[0];
return parcel;
}
diff --git a/microdroid/build.prop b/microdroid/build.prop
index 2caadbf..0908a4c 100644
--- a/microdroid/build.prop
+++ b/microdroid/build.prop
@@ -4,7 +4,7 @@
service.adb.listen_addrs=vsock:5555
# TODO(b/189164487): support build related properties
-ro.build.version.codename=Tiramisu
+ro.build.version.codename=UpsideDownCake
ro.build.version.release=13
ro.build.version.sdk=33
ro.build.version.security_patch=2022-06-05
diff --git a/tests/hostside/helper/java/android/virt/test/LogArchiver.java b/tests/hostside/helper/java/android/virt/test/LogArchiver.java
new file mode 100644
index 0000000..b6cae95
--- /dev/null
+++ b/tests/hostside/helper/java/android/virt/test/LogArchiver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 android.virt.test;
+
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
+
+import java.io.File;
+
+/** A helper class for archiving device log files to the host's tradefed output directory. */
+public abstract class LogArchiver {
+ /** Copy device log (then delete) to a tradefed output directory on the host.
+ *
+ * @param logs A {@link TestLogData} that needs to be owned by the actual test case.
+ * @param device The device to pull the log file from.
+ * @param remotePath The path on the device.
+ * @param localName Local file name to be copied to.
+ */
+ public static void archiveLogThenDelete(TestLogData logs, ITestDevice device, String remotePath,
+ String localName) throws DeviceNotAvailableException {
+ File logFile = device.pullFile(remotePath);
+ if (logFile != null) {
+ logs.addTestLog(localName, LogDataType.TEXT, new FileInputStreamSource(logFile));
+ // Delete to avoid confusing logs from a previous run, just in case.
+ device.deleteFile(remotePath);
+ }
+ }
+}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 440ae18..dc0284f 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -30,8 +30,6 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.TestDevice;
import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.FileInputStreamSource;
-import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
@@ -96,12 +94,7 @@
public static void archiveLogThenDelete(TestLogData logs, ITestDevice device, String remotePath,
String localName) throws DeviceNotAvailableException {
- File logFile = device.pullFile(remotePath);
- if (logFile != null) {
- logs.addTestLog(localName, LogDataType.TEXT, new FileInputStreamSource(logFile));
- // Delete to avoid confusing logs from a previous run, just in case.
- device.deleteFile(remotePath);
- }
+ LogArchiver.archiveLogThenDelete(logs, device, remotePath, localName);
}
// Run an arbitrary command in the host side and returns the result
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index c36e561..22b8a94 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -67,4 +67,9 @@
* Default is no mask which means a vCPU can run on any host CPU.
*/
@nullable String cpuAffinity;
+
+ /**
+ * List of task profile names to apply for the VM
+ */
+ String[] taskProfiles;
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index dfd3bff..83a81a0 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -63,4 +63,9 @@
* The format follows SemVer.
*/
@utf8InCpp String platformVersion;
+
+ /**
+ * List of task profile names to apply for the VM
+ */
+ String[] taskProfiles;
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index a2e856c..41cc4a5 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -467,6 +467,7 @@
memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
cpu_affinity: config.cpuAffinity.clone(),
+ task_profiles: config.taskProfiles.clone(),
console_fd,
log_fd,
indirect_files,
@@ -634,6 +635,7 @@
vm_config.protectedVm = config.protectedVm;
vm_config.numCpus = config.numCpus;
vm_config.cpuAffinity = config.cpuAffinity.clone();
+ vm_config.taskProfiles = config.taskProfiles.clone();
// Microdroid requires an additional payload disk image and the bootconfig partition.
if os_name == "microdroid" {
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index f1b179e..b184dca 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -64,6 +64,7 @@
pub memory_mib: Option<NonZeroU32>,
pub cpus: Option<NonZeroU32>,
pub cpu_affinity: Option<String>,
+ pub task_profiles: Vec<String>,
pub console_fd: Option<File>,
pub log_fd: Option<File>,
pub indirect_files: Vec<File>,
@@ -326,6 +327,10 @@
command.arg("--cpu-affinity").arg(cpu_affinity);
}
+ if !config.task_profiles.is_empty() {
+ command.arg("--task-profiles").arg(config.task_profiles.join(","));
+ }
+
// Keep track of what file descriptors should be mapped to the crosvm process.
let mut preserved_fds = config.indirect_files.iter().map(|file| file.as_raw_fd()).collect();
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 80ea9be..8b438b4 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -92,6 +92,10 @@
#[structopt(long)]
cpu_affinity: Option<String>,
+ /// Comma separated list of task profile names to apply to the VM
+ #[structopt(long)]
+ task_profiles: Vec<String>,
+
/// Paths to extra idsig files.
#[structopt(long = "extra-idsig")]
extra_idsigs: Vec<PathBuf>,
@@ -118,6 +122,10 @@
#[structopt(long)]
cpu_affinity: Option<String>,
+ /// Comma separated list of task profile names to apply to the VM
+ #[structopt(long)]
+ task_profiles: Vec<String>,
+
/// Path to file for VM console output.
#[structopt(long)]
console: Option<PathBuf>,
@@ -200,6 +208,7 @@
mem,
cpus,
cpu_affinity,
+ task_profiles,
extra_idsigs,
} => command_run_app(
service,
@@ -215,9 +224,10 @@
mem,
cpus,
cpu_affinity,
+ task_profiles,
&extra_idsigs,
),
- Opt::Run { config, daemonize, cpus, cpu_affinity, console, log } => {
+ Opt::Run { config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
command_run(
service,
&config,
@@ -227,6 +237,7 @@
/* mem */ None,
cpus,
cpu_affinity,
+ task_profiles,
)
}
Opt::Stop { cid } => command_stop(service, cid),
diff --git a/vm/src/run.rs b/vm/src/run.rs
index ef38d7d..3d3d703 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -54,6 +54,7 @@
mem: Option<u32>,
cpus: Option<u32>,
cpu_affinity: Option<String>,
+ task_profiles: Vec<String>,
extra_idsigs: &[PathBuf],
) -> Result<(), Error> {
let extra_apks = parse_extra_apk_list(apk, config_path)?;
@@ -105,6 +106,7 @@
memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
numCpus: cpus.unwrap_or(1) as i32,
cpuAffinity: cpu_affinity,
+ taskProfiles: task_profiles,
});
run(
service,
@@ -127,6 +129,7 @@
mem: Option<u32>,
cpus: Option<u32>,
cpu_affinity: Option<String>,
+ task_profiles: Vec<String>,
) -> Result<(), Error> {
let config_file = File::open(config_path).context("Failed to open config file")?;
let mut config =
@@ -138,6 +141,7 @@
config.numCpus = cpus as i32;
}
config.cpuAffinity = cpu_affinity;
+ config.taskProfiles = task_profiles;
run(
service,
&VirtualMachineConfig::RawConfig(config),