Reimplement ApkVerityTest
This is a regression/end-to-end test of fs-verity. The original test
setup uses ".fsv_sig" to enable fs-verity on APK then use them as the
target to tamper with.
Now that an app API exists, switch the test setup to use the API. We
can also avoid the placeholder app and signature now.
Since the test is almost a complete rewrite, move it from presubmit to
postsubmit until it's proven stable enough.
Bug: 277916185
Bug: 296465999
Test: atest FsVerityTest
Change-Id: I1bbeb5d6adc960bf3fe4e9c1262ffc7538062f61
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index ea21d51..ab669cc 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -48,9 +48,6 @@
"name":"CarrierAppIntegrationTestCases"
},
{
- "name":"ApkVerityTest"
- },
- {
"name":"CtsSilentUpdateHostTestCases"
},
{
diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 2d598dc..0af3b03 100644
--- a/core/java/com/android/internal/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
@@ -12,10 +12,6 @@
]
},
{
- "name": "ApkVerityTest",
- "file_patterns": ["VerityUtils\\.java"]
- },
- {
"name": "UpdatableSystemFontTest",
"file_patterns": ["VerityUtils\\.java"]
},
@@ -23,5 +19,11 @@
"name": "CtsApkVerityInstallHostTestCases",
"file_patterns": ["VerityUtils\\.java"]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "FsVerityTest",
+ "file_patterns": ["VerityUtils\\.java"]
+ }
]
}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index f026bea..53606a3 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -22,7 +22,7 @@
}
java_test_host {
- name: "ApkVerityTest",
+ name: "FsVerityTest",
srcs: ["src/**/*.java"],
libs: [
"tradefed",
@@ -30,8 +30,10 @@
"compatibility-host-util",
],
static_libs: [
+ "android.security.flags-aconfig-java-host",
"block_device_writer_jar",
"frameworks-base-hostutils",
+ "flag-junit-host",
],
test_suites: [
"general-tests",
@@ -41,14 +43,6 @@
"block_device_writer",
],
data: [
- ":ApkVerityTestCertDer",
- ":ApkVerityTestApp",
- ":ApkVerityTestAppFsvSig",
- ":ApkVerityTestAppDm",
- ":ApkVerityTestAppDmFsvSig",
- ":ApkVerityTestAppSplit",
- ":ApkVerityTestAppSplitFsvSig",
- ":ApkVerityTestAppSplitDm",
- ":ApkVerityTestAppSplitDmFsvSig",
+ ":FsVerityTestApp",
],
}
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 4487cef..49cbde0 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="APK fs-verity integration/regression test">
+<configuration description="fs-verity end-to-end test">
<option name="test-suite-tag" value="apct" />
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
@@ -24,19 +24,9 @@
<!-- This test requires root to write against block device. -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- Disable package verifier prevents it holding the target APK's fd that prevents cache
- eviction. -->
- <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
- <option name="restore-settings" value="true" />
-
- <!-- Skip in order to prevent reboot that confuses the test flow. -->
- <option name="force-skip-system-props" value="true" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" />
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FsVerityTestApp.apk"/>
+ <option name="cleanup-apks" value="true"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
@@ -48,9 +38,7 @@
<option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
</target_preparer>
- <!-- Skip on HWASan. TODO(b/232288278): Re-enable -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="ApkVerityTest.jar" />
+ <option name="jar" value="FsVerityTest.jar" />
</test>
</configuration>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
deleted file mode 100644
index 3f1a4f3..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.apkverity"
- android:isFeatureSplit="true"
- split="feature_x">
- <application>
- <activity android:name=".feature_x.DummyActivity"/>
- </application>
-</manifest>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
deleted file mode 100644
index fe91260..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.apkverity.feature_x;
-
-import android.app.Activity;
-
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
deleted file mode 100644
index a7bd771..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.apkverity;
-
-import android.app.Activity;
-
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/ApkVerityTest/FsVerityTestApp/Android.bp
similarity index 74%
rename from tests/ApkVerityTest/ApkVerityTestApp/Android.bp
rename to tests/ApkVerityTest/FsVerityTestApp/Android.bp
index adf8f9f..43da3ff 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
+++ b/tests/ApkVerityTest/FsVerityTestApp/Android.bp
@@ -22,17 +22,8 @@
}
android_test_helper_app {
- name: "ApkVerityTestApp",
- manifest: "AndroidManifest.xml",
- srcs: ["src/**/*.java"],
-}
-
-android_test_helper_app {
- name: "ApkVerityTestAppSplit",
- manifest: "feature_split/AndroidManifest.xml",
- srcs: ["src/**/*.java"],
- aaptflags: [
- "--custom-package com.android.apkverity.feature_x",
- "--package-id 0x80",
- ],
+ name: "FsVerityTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ static_libs: ["compatibility-device-util-axt"],
}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml b/tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
similarity index 75%
rename from tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
rename to tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
index 0b3ff77..42fe49b 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
+++ b/tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
@@ -16,8 +16,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.apkverity">
+ package="com.android.fsverity">
<application>
<activity android:name=".DummyActivity"/>
</application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.fsverity"
+ android:label="Helper app of fs-verity test">
+ </instrumentation>/>
</manifest>
diff --git a/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
new file mode 100644
index 0000000..2ed4fec
--- /dev/null
+++ b/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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 com.android.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.security.FileIntegrityManager;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Test helper that works with the host-side test to set up a test file, and to verify fs-verity
+ * verification is done expectedly.
+ */
+public class Helper {
+ private static final String TAG = "FsVerityTest";
+
+ private static final String FILENAME = "test.file";
+
+ private static final long BLOCK_SIZE = 4096;
+
+ @Test
+ public void prepareTest() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+
+ String basename = testArgs.getString("basename");
+ context.deleteFile(basename);
+
+ assertThat(testArgs).isNotNull();
+ int fileSize = Integer.parseInt(testArgs.getString("fileSize"));
+ Log.d(TAG, "Preparing test file with size " + fileSize);
+
+ byte[] bytes = new byte[8192];
+ Arrays.fill(bytes, (byte) '1');
+ try (FileOutputStream os = context.openFileOutput(basename, Context.MODE_PRIVATE)) {
+ for (int i = 0; i < fileSize; i += bytes.length) {
+ if (i + bytes.length > fileSize) {
+ os.write(bytes, 0, fileSize % bytes.length);
+ } else {
+ os.write(bytes);
+ }
+ }
+ }
+
+ // Enable fs-verity
+ FileIntegrityManager fim = context.getSystemService(FileIntegrityManager.class);
+ fim.setupFsVerity(context.getFileStreamPath(basename));
+ }
+
+ @Test
+ public void verifyFileRead() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+
+ // Collect indices that the backing blocks are supposed to be corrupted.
+ android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+ assertThat(testArgs).isNotNull();
+ String filePath = testArgs.getString("filePath");
+ String csv = testArgs.getString("brokenBlockIndicesCsv");
+ Log.d(TAG, "brokenBlockIndicesCsv: " + csv);
+ String[] strings = csv.split(",");
+ var corrupted = new ArrayList(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ corrupted.add(Integer.parseInt(strings[i]));
+ }
+
+ // Expect the read to succeed or fail per the prior.
+ try (var file = new RandomAccessFile(filePath, "r")) {
+ long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ for (int i = 0; i < (int) total_blocks; i++) {
+ file.seek(i * BLOCK_SIZE);
+ if (corrupted.contains(i)) {
+ Log.d(TAG, "Expecting read at block #" + i + " to fail");
+ assertThrows(IOException.class, () -> file.read());
+ } else {
+ assertThat(file.readByte()).isEqualTo('1');
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
index 72d9614..39944be 100644
--- a/tests/ApkVerityTest/TEST_MAPPING
+++ b/tests/ApkVerityTest/TEST_MAPPING
@@ -1,11 +1,11 @@
{
- "presubmit": [
+ "postsubmit": [
{
- "name": "ApkVerityTest"
+ "name": "FsVerityTest"
},
// nextgen test only runs during postsubmit.
{
- "name": "ApkVerityTest",
+ "name": "FsVerityTest",
"keywords": ["nextgen"]
}
]
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
deleted file mode 100644
index 482f633..0000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.apkverity;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.RootPermissionTest;
-
-import com.android.blockdevicewriter.BlockDeviceWriter;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This test makes sure app installs with fs-verity signature, and on-access verification works.
- *
- * <p>When an app is installed, all or none of the files should have their corresponding .fsv_sig
- * signature file. Otherwise, install will fail.
- *
- * <p>Once installed, file protected by fs-verity is verified by kernel every time a block is loaded
- * from disk to memory. The file is immutable by design, enforced by filesystem.
- *
- * <p>In order to make sure a block of the file is readable only if the underlying block on disk
- * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
- * address against the block device.
- *
- * <p>Requirements to run this test:
- * <ul>
- * <li>Device is rootable</li>
- * <li>The filesystem supports fs-verity</li>
- * <li>The feature flag is enabled</li>
- * </ul>
- */
-@RootPermissionTest
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class ApkVerityTest extends BaseHostJUnit4Test {
- private static final String TARGET_PACKAGE = "com.android.apkverity";
-
- private static final String BASE_APK = "ApkVerityTestApp.apk";
- private static final String BASE_APK_DM = "ApkVerityTestApp.dm";
- private static final String SPLIT_APK = "ApkVerityTestAppSplit.apk";
- private static final String SPLIT_APK_DM = "ApkVerityTestAppSplit.dm";
-
- private static final String INSTALLED_BASE_APK = "base.apk";
- private static final String INSTALLED_BASE_DM = "base.dm";
- private static final String INSTALLED_SPLIT_APK = "split_feature_x.apk";
- private static final String INSTALLED_SPLIT_DM = "split_feature_x.dm";
- private static final String INSTALLED_BASE_APK_FSV_SIG = "base.apk.fsv_sig";
- private static final String INSTALLED_BASE_DM_FSV_SIG = "base.dm.fsv_sig";
- private static final String INSTALLED_SPLIT_APK_FSV_SIG = "split_feature_x.apk.fsv_sig";
- private static final String INSTALLED_SPLIT_DM_FSV_SIG = "split_feature_x.dm.fsv_sig";
-
- private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
- private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
-
- /** Only 4K page is supported by fs-verity currently. */
- private static final int FSVERITY_PAGE_SIZE = 4096;
-
- private ITestDevice mDevice;
- private boolean mDmRequireFsVerity;
-
- @Before
- public void setUp() throws DeviceNotAvailableException {
- mDevice = getDevice();
- mDmRequireFsVerity = "true".equals(
- mDevice.getProperty("pm.dexopt.dm.require_fsverity"));
-
- expectRemoteCommandToSucceed("cmd file_integrity append-cert " + CERT_PATH);
- uninstallPackage(TARGET_PACKAGE);
- }
-
- @After
- public void tearDown() throws DeviceNotAvailableException {
- expectRemoteCommandToSucceed("cmd file_integrity remove-last-cert");
- uninstallPackage(TARGET_PACKAGE);
- }
-
- @Test
- public void testFsverityKernelSupports() throws DeviceNotAvailableException {
- ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
- expectRemoteCommandToSucceed("test -f /sys/fs/" + mountPoint.type + "/features/verity");
- }
-
- @Test
- public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK);
- }
-
- @Test
- public void testInstallBaseWithWrongSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFile(SPLIT_APK_DM + ".fsv_sig",
- BASE_APK + ".fsv_sig")
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithSplit()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(SPLIT_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK);
- }
-
- @Test
- public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM);
- }
-
- @Test
- public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFileAndSignature(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM);
- }
-
- @Test
- public void testInstallSplitOnly()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFileAndSignature(SPLIT_APK)
- .run();
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK);
- }
-
- @Test
- public void testInstallSplitOnlyMissingSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFile(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallSplitOnlyWithoutBaseSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(INSTALLED_BASE_APK);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFileAndSignature(SPLIT_APK)
- .run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- }
-
- @Test
- public void testInstallOnlyDmHasFsvSig()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_DM);
- }
-
- @Test
- public void testInstallDmWithoutFsvSig_Base()
- throws DeviceNotAvailableException, FileNotFoundException {
- InstallMultiple installer = new InstallMultiple()
- .addFile(BASE_APK)
- .addFile(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM);
- if (mDmRequireFsVerity) {
- installer.runExpectingFailure();
- } else {
- installer.run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM);
- }
- }
-
- @Test
- public void testInstallDmWithoutFsvSig_Split()
- throws DeviceNotAvailableException, FileNotFoundException {
- InstallMultiple installer = new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFile(SPLIT_APK_DM);
- if (mDmRequireFsVerity) {
- installer.runExpectingFailure();
- } else {
- installer.run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM);
- verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM);
- }
- }
-
- @Test
- public void testInstallSomeApkIsMissingFsvSig_Base()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallSomeApkIsMissingFsvSig_Split()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFileAndSignature(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithFsvSigThenSplitWithout()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .addFile(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithoutFsvSigThenSplitWith()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(INSTALLED_BASE_APK);
-
- new InstallMultiple()
- .addFileAndSignature(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testFsverityFileIsImmutableAndReadable() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- expectRemoteCommandToFail("echo -n '' >> " + apkPath);
- expectRemoteCommandToSucceed("cat " + apkPath + " > /dev/null");
- }
-
- @Test
- public void testFsverityFailToReadModifiedBlockAtFront() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- long apkSize = getFileSizeInBytes(apkPath);
- long offsetFirstByte = 0;
-
- // The first two pages should be both readable at first.
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
- if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
- offsetFirstByte + FSVERITY_PAGE_SIZE));
- }
-
- // Damage the file directly against the block device.
- damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
-
- // Expect actual read from disk to fail but only at damaged page.
- expectReadFromBlockDeviceToFail(apkPath, offsetFirstByte);
- if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- long lastByteOfTheSamePage =
- offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
- }
- }
-
- @Test
- public void testFsverityFailToReadModifiedBlockAtBack() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- long apkSize = getFileSizeInBytes(apkPath);
- long offsetOfLastByte = apkSize - 1;
-
- // The first two pages should be both readable at first.
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
- if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
- offsetOfLastByte - FSVERITY_PAGE_SIZE));
- }
-
- // Damage the file directly against the block device.
- damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
-
- // Expect actual read from disk to fail but only at damaged page.
- expectReadFromBlockDeviceToFail(apkPath, offsetOfLastByte);
- if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
- }
- }
-
- private void verifyInstalledFilesHaveFsverity(String... filenames)
- throws DeviceNotAvailableException {
- // Verify that all files are protected by fs-verity
- String apkPath = getApkPath(TARGET_PACKAGE);
- String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
- long kTargetOffset = 0;
- for (String basename : filenames) {
- String path = appDir + "/" + basename;
- damageFileAgainstBlockDevice(path, kTargetOffset);
-
- expectReadFromBlockDeviceToFail(path, kTargetOffset);
- }
- }
-
- private void expectReadFromBlockDeviceToFail(String readPath, long offset)
- throws DeviceNotAvailableException {
- // Retry is sometimes needed to pass the test. Package manager may have FD leaks
- // (see b/122744005 as example) that prevents the file in question to be evicted
- // from filesystem cache. Forcing GC workarounds the problem.
- int retry = 5;
- for (; retry > 0; retry--) {
- BlockDeviceWriter.dropCaches(mDevice);
- if (!BlockDeviceWriter.canReadByte(mDevice, readPath, offset)) {
- break;
- }
- try {
- String openFiles = expectRemoteCommandToSucceed("lsof " + readPath);
- CLog.d("lsof: " + openFiles);
- Thread.sleep(1000);
- forceGCOnOpenFilesProcess(getOpenFilesPIDs(openFiles));
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- assertTrue("Read from " + readPath + " should fail", retry > 0);
- }
-
- /**
- * This is a helper method that parses the lsof output to get PIDs of process holding FD.
- * Here is an example output of lsof. This method extracts the second columns(PID).
- *
- * Example lsof output:
- * COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
- * .example.app 1063 u0_a38 mem REG 253,6 8599 12826 example.apk
- * .example.app 1063 u0_a38 99r REG 253,6 8599 12826 example.apk
- */
- private Set<String> getOpenFilesPIDs(String lsof) {
- Set<String> openFilesPIDs = new HashSet<>();
- String[] lines = lsof.split("\n");
- for (int i = 1; i < lines.length; i++) {
- openFilesPIDs.add(lines[i].split("\\s+")[1]);
- }
- return openFilesPIDs;
- }
-
- /**
- * This is a helper method that forces GC on processes given their PIDs.
- * That is to execute shell command "kill -10" on PIDs.
- */
- private void forceGCOnOpenFilesProcess(Set<String> openFilesPIDs)
- throws DeviceNotAvailableException {
- for (String openFilePID : openFilesPIDs) {
- mDevice.executeShellV2Command("kill -10 " + openFilePID);
- }
- }
-
- private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
- String apkPath = getApkPath(TARGET_PACKAGE);
- String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
- // Exclude directories since we only care about files.
- HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
- expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n")));
-
- HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
- assertEquals(expectedFiles, actualFiles);
- }
-
- private void damageFileAgainstBlockDevice(String path, long offsetOfTargetingByte)
- throws DeviceNotAvailableException {
- assertTrue(path.startsWith("/data/"));
- ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
- ArrayList<String> args = new ArrayList<>();
- args.add(DAMAGING_EXECUTABLE);
- if ("f2fs".equals(mountPoint.type)) {
- args.add("--use-f2fs-pinning");
- }
- args.add(mountPoint.filesystem);
- args.add(path);
- args.add(Long.toString(offsetOfTargetingByte));
- expectRemoteCommandToSucceed(String.join(" ", args));
- }
-
- private String getApkPath(String packageName) throws DeviceNotAvailableException {
- String line = expectRemoteCommandToSucceed("pm path " + packageName + " | grep base.apk");
- int index = line.trim().indexOf(":");
- assertTrue(index >= 0);
- return line.substring(index + 1);
- }
-
- private long getFileSizeInBytes(String packageName) throws DeviceNotAvailableException {
- return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
- }
-
- private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(cmd);
- assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
- result.getStatus());
- return result.getStdout();
- }
-
- private void expectRemoteCommandToFail(String cmd) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(cmd);
- assertTrue("Unexpected success from `" + cmd + "`: " + result.getStderr(),
- result.getStatus() != CommandStatus.SUCCESS);
- }
-
- private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
- InstallMultiple() {
- super(getDevice(), getBuild());
- }
-
- InstallMultiple addFileAndSignature(String filename) {
- try {
- addFile(filename);
- addFile(filename + ".fsv_sig");
- } catch (FileNotFoundException e) {
- fail("Missing test file: " + e);
- }
- return this;
- }
- }
-}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java b/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
deleted file mode 100644
index 02e73d1..0000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.apkverity;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for invoking the install-multiple command via ADB. Subclass this for less typing:
- *
- * <code> private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> { public
- * InstallMultiple() { super(getDevice(), null); } } </code>
- */
-/*package*/ class BaseInstallMultiple<T extends BaseInstallMultiple<?>> {
-
- private final ITestDevice mDevice;
- private final IBuildInfo mBuild;
-
- private final List<String> mArgs = new ArrayList<>();
- private final Map<File, String> mFileToRemoteMap = new HashMap<>();
-
- /*package*/ BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo) {
- mDevice = device;
- mBuild = buildInfo;
- addArg("-g");
- }
-
- T addArg(String arg) {
- mArgs.add(arg);
- return (T) this;
- }
-
- T addFile(String filename) throws FileNotFoundException {
- return addFile(filename, filename);
- }
-
- T addFile(String filename, String remoteName) throws FileNotFoundException {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
- mFileToRemoteMap.put(buildHelper.getTestFile(filename), remoteName);
- return (T) this;
- }
-
- T inheritFrom(String packageName) {
- addArg("-r");
- addArg("-p " + packageName);
- return (T) this;
- }
-
- void run() throws DeviceNotAvailableException {
- run(true);
- }
-
- void runExpectingFailure() throws DeviceNotAvailableException {
- run(false);
- }
-
- private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
- final ITestDevice device = mDevice;
-
- // Create an install session
- final StringBuilder cmd = new StringBuilder();
- cmd.append("pm install-create");
- for (String arg : mArgs) {
- cmd.append(' ').append(arg);
- }
-
- String result = device.executeShellCommand(cmd.toString());
- TestCase.assertTrue(result, result.startsWith("Success"));
-
- final int start = result.lastIndexOf("[");
- final int end = result.lastIndexOf("]");
- int sessionId = -1;
- try {
- if (start != -1 && end != -1 && start < end) {
- sessionId = Integer.parseInt(result.substring(start + 1, end));
- }
- } catch (NumberFormatException e) {
- throw new IllegalStateException("Failed to parse install session: " + result);
- }
- if (sessionId == -1) {
- throw new IllegalStateException("Failed to create install session: " + result);
- }
-
- // Push our files into session. Ideally we'd use stdin streaming,
- // but ddmlib doesn't support it yet.
- for (final Map.Entry<File, String> entry : mFileToRemoteMap.entrySet()) {
- final File file = entry.getKey();
- final String remoteName = entry.getValue();
- final String remotePath = "/data/local/tmp/" + file.getName();
- if (!device.pushFile(file, remotePath)) {
- throw new IllegalStateException("Failed to push " + file);
- }
-
- cmd.setLength(0);
- cmd.append("pm install-write");
- cmd.append(' ').append(sessionId);
- cmd.append(' ').append(remoteName);
- cmd.append(' ').append(remotePath);
-
- result = device.executeShellCommand(cmd.toString());
- TestCase.assertTrue(result, result.startsWith("Success"));
- }
-
- // Everything staged; let's pull trigger
- cmd.setLength(0);
- cmd.append("pm install-commit");
- cmd.append(' ').append(sessionId);
-
- result = device.executeShellCommand(cmd.toString());
- if (expectingSuccess) {
- TestCase.assertTrue(result, result.contains("Success"));
- } else {
- TestCase.assertFalse(result, result.contains("Success"));
- }
- }
-}
diff --git a/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java
new file mode 100644
index 0000000..be479f2
--- /dev/null
+++ b/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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 com.android.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
+import android.security.Flags;
+
+import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This test verifies fs-verity works end-to-end. There is a corresponding helper app.
+ *
+ * <p>The helper app uses a FileIntegrityManager API to enable fs-verity to a file. The host test
+ * here * tampers with the file's backing storage, then tells the helper app to read and expect
+ * success/failure on read.
+ *
+ * <p>In order to make sure a block of the file is readable only if the underlying block on disk
+ * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
+ * address against the block device.
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+@RequiresFlagsEnabled(Flags.FLAG_FSVERITY_API)
+public class FsVerityHostTest extends BaseHostJUnit4Test {
+ private static final String TARGET_PACKAGE = "com.android.fsverity";
+
+ private static final String BASENAME = "test.file";
+ private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
+ @Test
+ public void testFsVeritySmallFile() throws Exception {
+ prepareTest(10000);
+
+ ITestDevice device = getDevice();
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192);
+ BlockDeviceWriter.dropCaches(device);
+
+ verifyRead(TARGET_PATH, "0,2");
+ }
+
+ @Test
+ public void testFsVerityLargerFileWithOneMoreMerkleTreeLevel() throws Exception {
+ prepareTest(128 * 4096 + 1);
+
+ ITestDevice device = getDevice();
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1);
+ BlockDeviceWriter.dropCaches(device);
+
+ verifyRead(TARGET_PATH, "1,100,128");
+ }
+
+ private void prepareTest(int fileSize) throws Exception {
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+ options.setTestClassName(TARGET_PACKAGE + ".Helper");
+ options.setTestMethodName("prepareTest");
+ options.addInstrumentationArg("basename", BASENAME);
+ options.addInstrumentationArg("fileSize", String.valueOf(fileSize));
+ assertThat(runDeviceTests(options)).isTrue();
+ }
+
+ private void verifyRead(String path, String indicesCsv) throws Exception {
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+ options.setTestClassName(TARGET_PACKAGE + ".Helper");
+ options.setTestMethodName("verifyFileRead");
+ options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
+ options.addInstrumentationArg("filePath", TARGET_PATH);
+ assertThat(runDeviceTests(options)).isTrue();
+ }
+}
diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/ApkVerityTest/testdata/Android.bp
index ccfc4c9..2d578d3 100644
--- a/tests/ApkVerityTest/testdata/Android.bp
+++ b/tests/ApkVerityTest/testdata/Android.bp
@@ -37,51 +37,3 @@
name: "ApkVerityTestCertDer",
srcs: ["ApkVerityTestCert.der"],
}
-
-filegroup {
- name: "ApkVerityTestAppDm",
- srcs: ["ApkVerityTestApp.dm"],
-}
-
-filegroup {
- name: "ApkVerityTestAppSplitDm",
- srcs: ["ApkVerityTestAppSplit.dm"],
-}
-
-genrule_defaults {
- name: "apk_verity_sig_gen_default",
- tools: ["fsverity"],
- tool_files: [":ApkVerityTestKeyPem", ":ApkVerityTestCertPem"],
- cmd: "$(location fsverity) sign $(in) $(out) " +
- "--key=$(location :ApkVerityTestKeyPem) " +
- "--cert=$(location :ApkVerityTestCertPem) " +
- "> /dev/null",
-}
-
-genrule {
- name: "ApkVerityTestAppFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestApp"],
- out: ["ApkVerityTestApp.apk.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppDmFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppDm"],
- out: ["ApkVerityTestApp.dm.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppSplitFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppSplit"],
- out: ["ApkVerityTestAppSplit.apk.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppSplitDmFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppSplitDm"],
- out: ["ApkVerityTestAppSplit.dm.fsv_sig"],
-}
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm b/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
deleted file mode 100644
index e53a861..0000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm b/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
deleted file mode 100644
index 75396f1..0000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/README.md b/tests/ApkVerityTest/testdata/README.md
deleted file mode 100644
index 163cb18..0000000
--- a/tests/ApkVerityTest/testdata/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-This test only runs on rooted / debuggable device.
-
-The test tries to install subsets of base.{apk,dm}, split.{apk,dm} and their
-corresponding .fsv_sig files (generated by build rule). If installed, the
-tests also tries to tamper with the file at absolute disk offset to verify
-if fs-verity is effective.
-
-How to generate dex metadata (.dm)
-==================================
-
- adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
- adb pull /data/local/tmp/primary.prof
- zip foo.dm primary.prof