Merge "CTS: fsv_sig install tests" into rvc-dev
diff --git a/hostsidetests/appsecurity/Android.bp b/hostsidetests/appsecurity/Android.bp
index 0c069d0..3cc61fe 100644
--- a/hostsidetests/appsecurity/Android.bp
+++ b/hostsidetests/appsecurity/Android.bp
@@ -50,6 +50,18 @@
         "CtsCorruptApkTests_Unaligned_R",
     ],
 
+    data: [
+        ":CtsApkVerityTestApp",
+        ":CtsApkVerityTestAppFsvSig",
+        ":CtsApkVerityTestAppDm",
+        ":CtsApkVerityTestAppDmFsvSig",
+        ":CtsApkVerityTestAppSplit",
+        ":CtsApkVerityTestAppSplitFsvSig",
+        ":CtsApkVerityTestAppSplitDm",
+        ":CtsApkVerityTestAppSplitDmFsvSig",
+        ":CtsApkVerityTestApp2",
+        ":CtsApkVerityTestApp2FsvSig",
+    ],
 }
 
 filegroup {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
new file mode 100644
index 0000000..a21cee3
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 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.appsecurity.cts;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.AppModeFull;
+import com.android.compatibility.common.util.CddTest;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public final class ApkVerityInstallTest extends BaseAppSecurityTest {
+
+    private static final String PACKAGE_NAME = "android.appsecurity.cts.apkveritytestapp";
+
+    private static final String BASE_APK = "CtsApkVerityTestApp.apk";
+    private static final String BASE_APK_DM = "CtsApkVerityTestApp.dm";
+    private static final String SPLIT_APK = "CtsApkVerityTestAppSplit.apk";
+    private static final String SPLIT_APK_DM = "CtsApkVerityTestAppSplit.dm";
+    private static final String BAD_BASE_APK = "CtsApkVerityTestApp2.apk";
+    private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2.dm";
+    private static final String FSV_SIG_SUFFIX = ".fsv_sig";
+    private static final String APK_VERITY_STANDARD_MODE = "2";
+
+    private static final HashMap<String, String> ORIGINAL_TO_INSTALL_NAME = new HashMap<>() {{
+        put(BASE_APK, "base.apk");
+        put(BASE_APK_DM, "base.dm");
+        put(SPLIT_APK, "split_feature_x.apk");
+        put(SPLIT_APK_DM, "split_feature_x.dm");
+    }};
+
+
+    @Before
+    public void setUp() throws DeviceNotAvailableException {
+        ITestDevice device = getDevice();
+        String apkVerityMode = device.getProperty("ro.apk_verity.mode");
+        assumeTrue(device.getLaunchApiLevel() >= 30
+                || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+    }
+
+    @CddTest(requirement="9.10/C-0-3")
+    @Test
+    public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK);
+    }
+
+    @CddTest(requirement="9.10/C-0-3")
+    @Test
+    public void testInstallBaseWithWrongSignature()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BAD_BASE_APK)
+                .addFile(BAD_BASE_APK + FSV_SIG_SUFFIX)
+                .runExpectingFailure();
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallBaseWithSplit()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK, SPLIT_APK);
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .addFile(BASE_APK_DM)
+                .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK, BASE_APK_DM);
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .addFile(BASE_APK_DM)
+                .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+                .addFile(SPLIT_APK_DM)
+                .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK, BASE_APK_DM, SPLIT_APK, SPLIT_APK_DM);
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallSplitOnly()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK);
+
+        new InstallMultiple()
+                .inheritFrom(PACKAGE_NAME)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK, SPLIT_APK);
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallSplitOnlyMissingSignature()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK);
+
+        new InstallMultiple()
+                .inheritFrom(PACKAGE_NAME)
+                .addFile(SPLIT_APK)
+                .runExpectingFailure();
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallSplitOnlyWithoutBaseSignature()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .run();
+
+        new InstallMultiple()
+                .inheritFrom(PACKAGE_NAME)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(SPLIT_APK);
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallOnlyBaseHasFsvSig()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .addFile(BASE_APK_DM)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK_DM)
+                .runExpectingFailure();
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallOnlyDmHasFsvSig()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK_DM)
+                .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK_DM)
+                .runExpectingFailure();
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallOnlySplitHasFsvSig()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK_DM)
+                .addFile(SPLIT_APK)
+                .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+                .addFile(SPLIT_APK_DM)
+                .runExpectingFailure();
+    }
+
+    @CddTest(requirement="9.10/C-0-3,C-1-1")
+    @Test
+    public void testInstallBaseWithFsvSigThenSplitWithout()
+            throws DeviceNotAvailableException, FileNotFoundException {
+        new InstallMultiple()
+                .addFile(BASE_APK)
+                .addFile(BASE_APK + FSV_SIG_SUFFIX)
+                .run();
+        verifyFsverityInstall(BASE_APK);
+
+        new InstallMultiple()
+                .addFile(SPLIT_APK)
+                .runExpectingFailure();
+    }
+
+    void verifyFsverityInstall(String... files) throws DeviceNotAvailableException {
+        DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME);
+        options.setTestClassName(PACKAGE_NAME + ".InstalledFilesCheck");
+        options.setTestMethodName("testFilesHaveFsverity");
+        options.addInstrumentationArg("Number",
+                Integer.toString(files.length));
+        for (int i = 0; i < files.length; ++i) {
+            String installName = ORIGINAL_TO_INSTALL_NAME.get(files[i]);
+            if (installName == null) {
+                fail("Install name is not defined for " + files[i]);
+            }
+            options.addInstrumentationArg("File" + i, installName);
+        }
+        runDeviceTests(options);
+    }
+
+    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+        InstallMultiple() {
+            super(getDevice(), getBuild(), getAbi());
+        }
+
+        @Override
+        protected String deriveRemoteName(String originalName, int index) {
+            return originalName;
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index 8acda6d..23f1840 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -46,7 +46,7 @@
     private final IAbi mAbi;
 
     private final List<String> mArgs = new ArrayList<>();
-    private final List<File> mApks = new ArrayList<>();
+    private final List<File> mFiles = new ArrayList<>();
     private final List<String> mSplits = new ArrayList<>();
     private boolean mUseNaturalAbi;
 
@@ -63,8 +63,12 @@
     }
 
     T addApk(String apk) throws FileNotFoundException {
+        return addFile(apk);
+    }
+
+    T addFile(String file) throws FileNotFoundException {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        mApks.add(buildHelper.getTestFile(apk));
+        mFiles.add(buildHelper.getTestFile(file));
         return (T) this;
     }
 
@@ -114,6 +118,10 @@
         return (T) this;
     }
 
+    protected String deriveRemoteName(String originalName, int index) {
+        return index + "_" + originalName;
+    }
+
     void run() throws DeviceNotAvailableException {
         run(true, null);
     }
@@ -157,17 +165,18 @@
 
         // Push our files into session. Ideally we'd use stdin streaming,
         // but ddmlib doesn't support it yet.
-        for (int i = 0; i < mApks.size(); i++) {
-            final File apk = mApks.get(i);
-            final String remotePath = "/data/local/tmp/" + i + "_" + apk.getName();
-            if (!device.pushFile(apk, remotePath)) {
-                throw new IllegalStateException("Failed to push " + apk);
+        for (int i = 0; i < mFiles.size(); i++) {
+            final File file = mFiles.get(i);
+            final String remoteName = deriveRemoteName(file.getName(), i);
+            final String remotePath = "/data/local/tmp/" + remoteName;
+            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(i + "_" + apk.getName());
+            cmd.append(' ').append(remoteName);
             cmd.append(' ').append(remotePath);
 
             result = device.executeShellCommand(cmd.toString());
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp
new file mode 100644
index 0000000..aca31e7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 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.
+
+android_test_helper_app {
+    name: "CtsApkVerityTestApp",
+    manifest: "AndroidManifest.xml",
+    srcs: ["src/**/*.java"],
+    jni_libs: [
+        "libCtsApkVerityTestAppJni",
+    ],
+    compile_multilib: "both",
+    libs: [
+        "junit",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "ctstestrunner-axt",
+    ],
+    dex_preopt: {
+        enabled: false,
+    },
+    use_embedded_native_libs: true,
+    sdk_version: "current",
+    certificate: ":cts-testkey1",
+}
+
+android_test_helper_app {
+    name: "CtsApkVerityTestAppSplit",
+    manifest: "feature_split/AndroidManifest.xml",
+    srcs: ["feature_split/src/**/*.java"],
+    aaptflags: [
+        "--custom-package android.appsecurity.cts.apkveritytestapp.feature_x",
+        "--package-id 0x80",
+    ],
+    dex_preopt: {
+        enabled: false,
+    },
+    sdk_version: "current",
+    certificate: ":cts-testkey1",
+}
+
+cc_library_shared {
+    name: "libCtsApkVerityTestAppJni",
+    srcs: ["jni/**/*.cpp"],
+    shared_libs: [
+        "libnativehelper_compat_libc++",
+        "liblog",
+    ],
+    static_libs: [
+        "libbase_ndk",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    stl: "c++_static",
+    sdk_version: "current",
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..5331c28
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2020 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="android.appsecurity.cts.apkveritytestapp">
+    <application>
+        <activity android:name=".DummyActivity"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.appsecurity.cts.apkveritytestapp"
+                     android:label="CTS test app of apk verity">
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>/>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml
new file mode 100644
index 0000000..bec126d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2020 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="android.appsecurity.cts.apkveritytestapp"
+    android:isFeatureSplit="true"
+    split="feature_x">
+    <application>
+        <activity android:name=".feature_x.DummyActivity"/>
+    </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java
new file mode 100644
index 0000000..ab0faf7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 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.appsecurity.cts.apkveritytestapp.feature_x;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp
new file mode 100644
index 0000000..abc04d9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "ApkVerityTestApp"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <android/log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck_hasFsverityNative(
+    JNIEnv *env, jobject /*thiz*/, jstring filePath) {
+  ScopedUtfChars path(env, filePath);
+
+  struct statx out = {};
+  if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+    ALOGE("statx failed at %s", path.c_str());
+    return JNI_FALSE;
+  }
+
+  // Sanity check.
+  if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+    ALOGE("STATX_ATTR_VERITY not supported by kernel");
+    return JNI_FALSE;
+  }
+
+  return (out.stx_attributes & STATX_ATTR_VERITY) != 0 ? JNI_TRUE : JNI_FALSE;
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java
new file mode 100644
index 0000000..ff4f298
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 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.appsecurity.cts.apkveritytestapp;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java
new file mode 100644
index 0000000..c69f20b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.appsecurity.cts.apkveritytestapp;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+
+public class InstalledFilesCheck {
+    private static final String TAG = InstalledFilesCheck.class.getSimpleName();
+    private static final String PACKAGE_NAME = InstalledFilesCheck.class.getPackage().getName();
+
+    static {
+        System.loadLibrary("CtsApkVerityTestAppJni");
+    }
+
+    private static native boolean hasFsverityNative(@NonNull String path);
+
+    @Test
+    public void testFilesHaveFsverity() {
+        for (String path : getInterestedFiles()) {
+            assertTrue("Expect file installed: " + path, new File(path).exists());
+            assertTrue("Expect to have fs-verity: " + path, hasFsverityNative(path));
+        }
+    }
+
+    private ArrayList<String> getInterestedFiles() {
+        Context context = InstrumentationRegistry.getContext();
+        Path dirname = Paths.get(context.getApplicationInfo().sourceDir).getParent();
+        ArrayList<String> output = new ArrayList<>();
+
+        android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+        assertNotEquals(testArgs, null);
+        int number = Integer.valueOf(testArgs.getString("Number"));
+        assertTrue(number > 0);
+
+        for (int i = 0; i < number; ++i) {
+            String basename = testArgs.getString("File" + Integer.toString(i));
+            String filename = dirname.resolve(basename).toString();
+            Log.d(TAG, "Adding interested file " + filename);
+            output.add(filename);
+        }
+        return output;
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
new file mode 100644
index 0000000..3fc6f86
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
@@ -0,0 +1,89 @@
+// Copyright (C) 2020 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.
+
+filegroup {
+    name: "CtsApkVerityTestAppDm",
+    srcs: ["CtsApkVerityTestApp.dm"],
+}
+
+filegroup {
+    name: "CtsApkVerityTestAppSplitDm",
+    srcs: ["CtsApkVerityTestAppSplit.dm"],
+}
+
+filegroup {
+    name: "fsverity-debug-key",
+    srcs: ["fsverity-debug-key.pem"],
+}
+
+filegroup {
+    name: "fsverity-debug-cert",
+    srcs: ["fsverity-debug.x509.pem"],
+}
+
+genrule_defaults {
+    name: "cts_apk_verity_sig_gen_default",
+    tools: ["fsverity"],
+    tool_files: [
+        ":fsverity-debug-key",
+        ":fsverity-debug-cert",
+    ],
+    cmd: "$(location fsverity) sign $(in) $(out) " +
+        "--key=$(location :fsverity-debug-key) " +
+        "--cert=$(location :fsverity-debug-cert) " +
+        "> /dev/null",
+}
+
+genrule {
+    name: "CtsApkVerityTestAppFsvSig",
+    defaults: ["cts_apk_verity_sig_gen_default"],
+    srcs: [":CtsApkVerityTestApp"],
+    out: ["CtsApkVerityTestApp.apk.fsv_sig"],
+}
+
+genrule {
+    name: "CtsApkVerityTestAppDmFsvSig",
+    defaults: ["cts_apk_verity_sig_gen_default"],
+    srcs: [":CtsApkVerityTestAppDm"],
+    out: ["CtsApkVerityTestApp.dm.fsv_sig"],
+}
+
+genrule {
+    name: "CtsApkVerityTestAppSplitFsvSig",
+    defaults: ["cts_apk_verity_sig_gen_default"],
+    srcs: [":CtsApkVerityTestAppSplit"],
+    out: ["CtsApkVerityTestAppSplit.apk.fsv_sig"],
+}
+
+genrule {
+    name: "CtsApkVerityTestAppSplitDmFsvSig",
+    defaults: ["cts_apk_verity_sig_gen_default"],
+    srcs: [":CtsApkVerityTestAppSplitDm"],
+    out: ["CtsApkVerityTestAppSplit.dm.fsv_sig"],
+}
+
+// Create another app with mismatched .fsv_sig
+genrule {
+    name: "CtsApkVerityTestApp2",
+    srcs: [":CtsApkVerityTestApp"],
+    out: ["CtsApkVerityTestApp2.apk"],
+    cmd: "cp -f $(in) $(out)"
+}
+
+genrule {
+    name: "CtsApkVerityTestApp2FsvSig",
+    srcs: [":CtsApkVerityTestAppSplitFsvSig"],
+    out: ["CtsApkVerityTestApp2.apk.fsv_sig"],
+    cmd: "cp -f $(in) $(out)"
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md
new file mode 100644
index 0000000..a0be4f9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md
@@ -0,0 +1,7 @@
+How to generate dex metadata (.dm) with profile for testing
+===========================================================
+```
+adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
+adb pull /data/local/tmp/primary.prof
+zip foo.dm primary.prof
+```
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem
new file mode 100644
index 0000000..a3b4019
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+Ay3LwFyjYVrcHTlmGYYR6HVN6my098ymjg==
+-----END PRIVATE KEY-----
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der
new file mode 100644
index 0000000..fbee6f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem
new file mode 100644
index 0000000..d4fbea5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIUMwFudVZlac6hU7ClbIJXRUiXOIUwDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMB4XDTIwMDQwMzIxMzEwM1oXDTIwMDUwMzIxMzEwM1owgZQxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp
+biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD
+VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4DLcnCcpDlhm7xq6TCOQ
+XNw7CAItEiLaaWkIXA5yOSLfPSBl77OWFt1thEXaZCSKmyVYy2gnSJ7dDYSworZ3
+M/Nrb47J73/yM2oO5FUrt8j9Vx/zwzJaG7nhtBZIZHEf4Fg6/Yw50+5ny7kjllCr
+5qcFd4v7B7BJs7DQKOiXeWYe4GfEn7We7hfVENcEU0x02A3gwxMM8ZLi1qdHqdOo
+FvXrdbRVLQ/lGEjdsngjczoCv2nZeC49IykksNiiM4atmMMWaik8d/Sd5R9z/s2k
+bou8prEpEuP9PyXgi7ASGrtdvMBw4PY2NKVqgXlYhVuQcCreYLi7Dwu8A/VKOJbX
+XayXrbQ8FjOdEHTz3tVpctv7Js1E/W7QDxp3isnG3XuR4bfLo3NjTmEXcSNGrrAv
+vawvUbqCMr5VJEzKnaGS0A9vMg1aEmqTwPTEgmUVRmahyAVn4uVgQDGIqc4XNTvL
+3NZ/C8AcNlHQxbHjg6se8tTQe+U05aMTuL2eS8jfxLFxonD7SqpCZckBqXAsYLXl
+efLw9GpNy3bOFZavFVC4wZqliEW6HfPJW6ub5qYVkwS6Wp9IWYJRWzE5M3Kg4Ez/
+56O/nzSNu0MEWz5eDcimPZawnbmjxBi7D3lGIidgSFJfKc4SJaz5wR0fybKX+3Ys
+hLDfNNqhbTRh4ZD/Awa5ra8CAwEAAaNTMFEwHQYDVR0OBBYEFFJpY9e3fqe+38d5
+M8XP2u4zCiGWMB8GA1UdIwQYMBaAFFJpY9e3fqe+38d5M8XP2u4zCiGWMA8GA1Ud
+EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADcNIAiaUtZbOPLZLgv8sUHk
+QdBuKmNrQKvS91HSWIkQ0pK/5jcemAkTI3geF+cO3lBCiJRYWyF3d09onHvJXtid
+ASoT89W+BxJSIiPXOhcpLST7RqLVqgiyg0nATCzHa4euTKHFwTNll3pIG06QTcPg
+mXxTG5wJqztAq3E3chZewrWuN7leVvaWE2jeI3x3IpaH+Gyul3LOBVC70FMIwfTt
+/HEFwawI+7t1aQU9Mk7VUSFX4nsoN7ZhicUu+WF5nuZpAIlJvM03HGTlr8/IbPIg
+yXiqx8//mgiC/eO/Pwy1kgPJ7Mfq5Fy3eMRIwUDqEMOgtcVAz/s6QdEKzkI0byPK
+LNs/eSI5lJ8qq8E4xtSIH4vSginvfYX+S6VQvIf1VqSkKh3NMTCrQ1768h+qLtyx
+yIdfSTjq1YD4IwQv7NInkVd0TdJf03BpmS0Q6wbici/6QZmK96et/BrnbsW6lXpB
+qBr2L9Xnr0p9jnJO+xu0HYJXsg1xZ6kE/SKnE8EPQIjuqDBtcO9kYBwF0jGj/srk
+2KK4SRxyhbdumJ7bNk4Y4R6h3c6LubUmNGudizahnxBm3sfQk275ppig0qANDl10
+wnKknFYY54g/AzTC2deXrIJXFw+Mb7XZbEUmFtU8gZT8r6vv3tpMmLD2mL8FyuDP
+k0bCIojaAS2Ufxxc1SuH
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
new file mode 100644
index 0000000..968675c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
@@ -0,0 +1,28 @@
+How the test works
+==================
+ApkVerityTestApp is a test helper app to be installed with fs-verity signature
+file (.fsv\_sig). In order for this CTS test to run on a release build across
+vendors, the signature needs to be verified against a release certificate loaded
+to kernel.
+
+How to modify the test helper app
+=================================
+Modifying the test helper app will also require to sign the apk with a local debug
+key. You will also need to point the test to use your local build.
+
+How to load debug key
+---------------------
+On debuggable build, it can be done by:
+
+```
+adb root
+adb shell 'mini-keyctl padd asymmetric fsv-play .fs-verity' < fsverity-debug.x509.der
+```
+
+On user build, the keyring is closed and doesn't accept extra key. A workaround
+is to copy the .der file to /system/etc/security/fsverity. Upon reboot, the
+certificate will be loaded to kernel as usual.
+
+How to use the app built locally
+--------------------------------
+TODO: provide instruction once the test switches to prebuilt and release signature.