Validate update of vendor apex at install time

Bug: 254570732
Test: ApexdHostTest, ApexTestCases, libvintf_test, ApexServiceTestCases

Apexd performs an additional check on incoming vendor apexes,
using the VintfObject library to compatibility-check after
adding the vintf_fragment data from the incoming apex.

Change-Id: I94d842434fc0912beb2754c05878174ccde84433
diff --git a/apexd/Android.bp b/apexd/Android.bp
index 685c80d..596b241 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -80,6 +80,7 @@
   shared_libs: [
     "liblog",
     "liblogwrap",
+    "libvintf",
   ],
   static_libs: [
     "libapex",
@@ -187,6 +188,7 @@
     "apexd_private.cpp",
     "apexd_session.cpp",
     "apexd_verity.cpp",
+    "apexd_vendor_apex.cpp",
   ],
   export_include_dirs: ["."],
   generated_sources: ["apex-info-list-tinyxml"],
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index 08bd7bb..1f32f5e 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -48,6 +48,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <utils/Trace.h>
+#include <vintf/VintfObject.h>
 
 #include <algorithm>
 #include <array>
@@ -83,6 +84,7 @@
 #include "apexd_rollback_utils.h"
 #include "apexd_session.h"
 #include "apexd_utils.h"
+#include "apexd_vendor_apex.h"
 #include "apexd_verity.h"
 #include "com_android_apex.h"
 
@@ -937,6 +939,9 @@
     if (apex_file.GetManifest().name() == kSepolicyApexName) {
       return CopySepolicyToMetadata(mount_point);
     }
+    if (IsVendorApex(apex_file)) {
+      return CheckVendorApexUpdate(apex_file, mount_point);
+    }
     return Result<void>{};
   };
   return RunVerifyFnInsideTempMount(apex_file, validate_fn, false);
@@ -3868,6 +3873,9 @@
         dirs->end()) {
       return Error() << apex_file.GetPath() << " contains priv-app inside";
     }
+    if (IsVendorApex(apex_file)) {
+      return CheckVendorApexUpdate(apex_file, mount_point);
+    }
     return Result<void>{};
   };
   return RunVerifyFnInsideTempMount(apex_file, check_fn, true);
diff --git a/apexd/apexd_vendor_apex.cpp b/apexd/apexd_vendor_apex.cpp
new file mode 100644
index 0000000..68a439f
--- /dev/null
+++ b/apexd/apexd_vendor_apex.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * This file contains the vendor-apex-specific functions of apexd
+ */
+
+#include "apexd_vendor_apex.h"
+
+#include <android-base/strings.h>
+#include <vintf/VintfObject.h>
+
+#include "apex_file_repository.h"
+#include "apexd_private.h"
+
+using android::base::Error;
+using android::base::StartsWith;
+
+namespace android {
+namespace apex {
+
+// Returns if apex is a vendor apex, works by testing path of its preinstalled
+// version NOTE: If BOARD_USES_VENDORIMAGE is false, then /vendor will be a
+// symlink to
+//    /system/vendor. Apexd handles "realpath"s for apexes. Hence when checking
+//    if an Apex is a vendor apex with path, we need to check against both.
+bool IsVendorApex(const ApexFile& apex_file) {
+  const auto& instance = ApexFileRepository::GetInstance();
+  const auto& preinstalled =
+      instance.GetPreInstalledApex(apex_file.GetManifest().name());
+  const auto& preinstalled_path = preinstalled.get().GetPath();
+  return (StartsWith(preinstalled_path, "/vendor/apex/") ||
+          StartsWith(preinstalled_path, "/system/vendor/apex/"));
+}
+
+// Checks Compatibility for incoming vendor apex.
+//    Adds the data from apex's vintf_fragment(s) and tests compatibility.
+base::Result<void> CheckVendorApexUpdate(const ApexFile& apex_file,
+                                         const std::string& apex_mount_point) {
+  std::string error;
+
+  const std::string apex_name = apex_file.GetManifest().name();
+
+  std::string path_to_replace =
+      apexd_private::GetActiveMountPoint(apex_file.GetManifest());
+
+  // Create PathReplacingFileSystem instance containing caller's path
+  // substitution
+  std::unique_ptr<vintf::FileSystem> path_replaced_fs =
+      std::make_unique<vintf::details::PathReplacingFileSystem>(
+          std::move(path_to_replace), apex_mount_point,
+          std::make_unique<vintf::details::FileSystemImpl>());
+
+  // Create a new VintfObject that uses our path-replacing FileSystem instance
+  auto vintf_with_replaced_path =
+      vintf::VintfObject::Builder()
+          .setFileSystem(std::move(path_replaced_fs))
+          .build();
+
+  // Disable RuntimeInfo components. Allows callers to run check
+  // without requiring read permission of restricted resources
+  auto flags = vintf::CheckFlags::DEFAULT;
+  flags = flags.disableRuntimeInfo();
+
+  // checkCompatibility on vintfObj using the replacement vintf directory
+  int ret = vintf_with_replaced_path->checkCompatibility(&error, flags);
+  LOG(DEBUG) << "CheckVendorApexUpdate: check on vendor apex " << apex_name
+             << " returned " << ret << " (want " << vintf::COMPATIBLE
+             << " == COMPATIBLE)";
+  if (ret == vintf::INCOMPATIBLE) {
+    return Error() << "vendor apex is not compatible, error=" << error;
+  } else if (ret != vintf::COMPATIBLE) {
+    return Error() << "Check of vendor apex failed, error=" << error;
+  }
+
+  return {};
+}
+
+}  // namespace apex
+}  // namespace android
diff --git a/apexd/apexd_vendor_apex.h b/apexd/apexd_vendor_apex.h
new file mode 100644
index 0000000..4901b02
--- /dev/null
+++ b/apexd/apexd_vendor_apex.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_APEXD_VENDOR_APEX_H_
+#define ANDROID_APEXD_VENDOR_APEX_H_
+
+#include <android-base/result.h>
+
+#include <string>
+
+#include "apex_file.h"
+
+using android::base::Result;
+
+namespace android {
+namespace apex {
+
+// Determines if an incoming apex is a vendor apex
+bool IsVendorApex(const ApexFile& apex_file);
+
+// For incoming vendor apex updates.  Adds the data from its
+//   vintf_fragment(s) and tests compatibility.
+Result<void> CheckVendorApexUpdate(const ApexFile& apex_file,
+                                   const std::string& apex_mount_point);
+
+}  // namespace apex
+}  // namespace android
+
+#endif  // ANDROID_APEXD_VENDOR_APEX_H_
diff --git a/tests/Android.bp b/tests/Android.bp
index de39b63..b1a87bc 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -167,6 +167,10 @@
         ":com.android.apex.cts.shim.v2_prebuilt",
         ":com.android.apex.cts.shim.v2_no_pb",
         ":com.android.apex.cts.shim.v2_additional_file_prebuilt",
+        ":test.good1.com.android.hardware.wifi",
+        ":test.bad1.com.android.hardware.wifi",
+        ":test.bad2.com.android.hardware.wifi",
+        ":test.bad3.com.android.hardware.wifi",
     ],
 }
 
diff --git a/tests/src/com/android/tests/apex/ApexdHostTest.java b/tests/src/com/android/tests/apex/ApexdHostTest.java
index 2d72e9f..6018404 100644
--- a/tests/src/com/android/tests/apex/ApexdHostTest.java
+++ b/tests/src/com/android/tests/apex/ApexdHostTest.java
@@ -75,6 +75,11 @@
         }
     }
 
+    private boolean deviceHasActiveApex(String apexName) throws Exception {
+        return getDevice().getActiveApexes().stream().anyMatch(
+                apex -> apex.name.equals(apexName));
+    }
+
     @Test
     public void testOrphanedApexIsNotActivated() throws Exception {
         assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
@@ -339,4 +344,174 @@
             getDevice().deleteFile("/data/apex/active/com.android.apex.cts.shim@2.apex");
         }
     }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex update with an
+     *     updatable-via-apex value that doesn't match the apex's interface.
+     *     Install method is reboot-needing (staged) installation.
+     */
+    @Test
+    public void testRejectsStagedApexWithIncorrectUpdatableViaApexValue() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad1.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get an error
+        String error = mHostUtils.installStagedPackage(apexFile);
+        assertThat(error).isNotNull();
+        // Verify error text involves the device manifest (Note the actual
+        //   parser error is visible in the log, but it doesn't get passed
+        //   up through libvintf, so we end up with a manifest-related error)
+        assertThat(error).contains("No device manifest");
+    }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex update with a
+     *     vintf fragment containing syntax-invalid XML. Staged installation.
+     */
+    @Test
+    public void testRejectsStagedApexWithInvalidSyntaxVintfFragment() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad2.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get our device manifest error
+        String error = mHostUtils.installStagedPackage(apexFile);
+        assertThat(error).isNotNull();
+        assertThat(error).contains("No device manifest");
+    }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex that tries to
+     *     update an unrelated hardware interface.  Staged installation.
+     */
+    @Test
+    public void testRejectsStagedApexThatUpdatesUnrelatedHardware() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad3.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get a manifest/matix compatibility error
+        String error = mHostUtils.installStagedPackage(apexFile);
+        assertThat(error).isNotNull();
+        assertThat(error).contains(
+                "Device manifest and framework compatibility matrix are incompatible");
+    }
+
+    /**
+     * Test to verify that apexd will accept a good vendor apex update
+     *     Install method is immediate (rebootless) (non-staged) installation.
+     */
+    @Test
+    public void testAcceptsGoodRebootlessApex() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.good1.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get an error
+        String error = mHostUtils.installRebootlessPackage(apexFile);
+        assertThat(error).isNull();
+    }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex update with an
+     *     updatable-via-apex value that doesn't match the apex's interface.
+     *     Install method is immediate (rebootless) (non-staged) installation.
+     */
+    @Test
+    public void testRejectsRebootlessApexWithIncorrectUpdatableViaApexValue() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad1.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get an error
+        String error = mHostUtils.installRebootlessPackage(apexFile);
+        assertThat(error).isNotNull();
+        // Verify error text involves the device manifest (Note the actual
+        //   parser error is visible in the log, but it doesn't get passed
+        //   up through libvintf, so we end up with a manifest-related error)
+        assertThat(error).contains("No device manifest");
+    }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex update with a
+     *     vintf fragment containing syntax-invalid XML.
+     */
+    @Test
+    public void testRejectsRebootlessApexWithInvalidSyntaxVintfFragment() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad2.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get our device manifest error
+        String error = mHostUtils.installRebootlessPackage(apexFile);
+        assertThat(error).isNotNull();
+        assertThat(error).contains("No device manifest");
+    }
+
+    /**
+     * Test to verify that apexd will reject a vendor apex tries to
+     *     update an unrelated hardware interface.
+     */
+    @Test
+    public void testRejectsRebootlessApexThatUpdatesUnrelatedHardware() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+        assumeTrue("Device requires root", getDevice().isAdbRoot());
+        assumeTrue("Device test requires wifi hardware",
+                getDevice().hasFeature("android.hardware.wifi"));
+        assumeTrue("Device test requires an active wifi apex",
+                deviceHasActiveApex("com.android.hardware.wifi"));
+
+        String apex_filename = "test.bad3.com.android.hardware.wifi.apex";
+
+        File apexFile = mHostUtils.getTestFile(apex_filename);
+
+        // Try to install it, we should get a manifest/matix compatibility error
+        String error = mHostUtils.installRebootlessPackage(apexFile);
+        assertThat(error).isNotNull();
+        assertThat(error).contains(
+                "Device manifest and framework compatibility matrix are incompatible");
+    }
 }
diff --git a/tests/testdata/vendorapex/Android.bp b/tests/testdata/vendorapex/Android.bp
index f678c32..aa9d781 100644
--- a/tests/testdata/vendorapex/Android.bp
+++ b/tests/testdata/vendorapex/Android.bp
@@ -67,7 +67,7 @@
 cc_binary {
     name: "apex_vendor_foo_test_binary",
     shared_libs: [
-        "libbinder_ndk",  // will add "requireNativeLibs"
+        "libbinder_ndk", // will add "requireNativeLibs"
     ],
     srcs: [
         "apex_vendor_foo_test_binary.cpp",
@@ -176,3 +176,104 @@
         "apex_vendor_foo_v2_vintf",
     ],
 }
+
+// Test apex for updating com.android.hardware.wifi, with an
+//    updatable-via-apex value that doesn't match wifi's interface.
+apex {
+    name: "test.bad1.com.android.hardware.wifi",
+    manifest: "wifi_manifest_rebootless.json",
+    key: "com.android.hardware.wifi.key",
+    certificate: ":com.android.hardware.wifi.certificate",
+    file_contexts: "wifi_file_contexts",
+    use_vndk_as_stable: true,
+    updatable: false,
+    soc_specific: true,
+    installable: false,
+    prebuilts: [
+        "vintf_fragment_wifi_bad1.xml",
+        "com.android.hardware.wifi.rc",
+    ],
+}
+
+prebuilt_etc {
+    name: "vintf_fragment_wifi_bad1.xml",
+    src: "vintf_fragment_wifi_bad1.xml",
+    installable: false,
+    sub_dir: "vintf", // Puts fragment into etc/vintf
+}
+
+// Creates wifi test apex where its vintf fragment has invalid XML syntax
+//    (an unclosed tag)
+apex {
+    name: "test.bad2.com.android.hardware.wifi",
+    manifest: "wifi_manifest_rebootless.json",
+    key: "com.android.hardware.wifi.key",
+    certificate: ":com.android.hardware.wifi.certificate",
+    file_contexts: "wifi_file_contexts",
+    use_vndk_as_stable: true,
+    updatable: false,
+    soc_specific: true,
+    installable: false,
+    prebuilts: [
+        "vintf_fragment_wifi_bad2.xml",
+        "com.android.hardware.wifi.rc",
+    ],
+}
+
+prebuilt_etc {
+    name: "vintf_fragment_wifi_bad2.xml",
+    src: "vintf_fragment_wifi_bad2.xml",
+    installable: false,
+    sub_dir: "vintf",
+}
+
+// Creates wifi test apex that is updating interface for other hardware
+//    (picked an HAL that exists elsewhere, and for hardware that has
+//     updatable-via-apex="true", and still gets caught - good!)
+apex {
+    name: "test.bad3.com.android.hardware.wifi",
+    manifest: "wifi_manifest_rebootless.json",
+    key: "com.android.hardware.wifi.key",
+    certificate: ":com.android.hardware.wifi.certificate",
+    file_contexts: "wifi_file_contexts",
+    use_vndk_as_stable: true,
+    updatable: false,
+    soc_specific: true,
+    installable: false,
+    prebuilts: [
+        "vintf_fragment_wifi_bad3.xml",
+        "com.android.hardware.wifi.rc",
+    ],
+}
+
+prebuilt_etc {
+    name: "vintf_fragment_wifi_bad3.xml",
+    src: "vintf_fragment_wifi_bad3.xml",
+    installable: false,
+    sub_dir: "vintf",
+}
+
+// Test apex for updating com.android.hardware.wifi, with a
+//    good apex
+apex {
+    name: "test.good1.com.android.hardware.wifi",
+    manifest: "wifi_manifest_rebootless.json",
+    key: "com.android.hardware.wifi.key",
+    certificate: ":com.android.hardware.wifi.certificate",
+    file_contexts: "wifi_file_contexts",
+    use_vndk_as_stable: true,
+    updatable: false,
+    soc_specific: true,
+    installable: false,
+    prebuilts: [
+        "vintf_fragment_wifi_good1.xml",
+        "com.android.hardware.wifi.rc",
+    ],
+}
+
+prebuilt_etc {
+    name: "vintf_fragment_wifi_good1.xml",
+    src: "vintf_fragment_wifi_service.xml",
+    installable: false,
+    sub_dir: "vintf", // Puts fragment into etc/vintf
+}
diff --git a/tests/testdata/vendorapex/vintf_fragment_wifi_bad1.xml b/tests/testdata/vendorapex/vintf_fragment_wifi_bad1.xml
new file mode 100644
index 0000000..a68fea5
--- /dev/null
+++ b/tests/testdata/vendorapex/vintf_fragment_wifi_bad1.xml
@@ -0,0 +1,16 @@
+<!--
+This file is a copy of hardware/interfaces/wifi/1.6/default/android.hardware.wifi@1.0-service.xml,
+but modified to have an invalid updatable-via-apex value. Attempts to install a vendor apex
+that includes this file as a vintf fragment should fail.
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl" updatable-via-apex="android.hardware.potato">
+        <name>android.hardware.wifi</name>
+        <transport>hwbinder</transport>
+        <version>1.6</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tests/testdata/vendorapex/vintf_fragment_wifi_bad2.xml b/tests/testdata/vendorapex/vintf_fragment_wifi_bad2.xml
new file mode 100644
index 0000000..80c451d
--- /dev/null
+++ b/tests/testdata/vendorapex/vintf_fragment_wifi_bad2.xml
@@ -0,0 +1,16 @@
+<!--
+This file is a copy of hardware/interfaces/wifi/1.6/default/android.hardware.wifi@1.0-service.xml,
+but modified to have an xml syntax error (unclosed <version> tag).
+Attempts to install a vendor apex that includes this file as a vintf fragment should fail.
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.wifi</name>
+        <transport>hwbinder</transport>
+        <version 1.6</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tests/testdata/vendorapex/vintf_fragment_wifi_bad3.xml b/tests/testdata/vendorapex/vintf_fragment_wifi_bad3.xml
new file mode 100644
index 0000000..1a5a1ba
--- /dev/null
+++ b/tests/testdata/vendorapex/vintf_fragment_wifi_bad3.xml
@@ -0,0 +1,16 @@
+<!--
+This file is a copy of hardware/interfaces/wifi/1.6/default/android.hardware.wifi@1.0-service.xml,
+modified to have a different valid interface name, but which is not related to the wifi hal interface.
+Attempts to install a vendor apex that includes this file as a vintf fragment should fail.
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.wifi</name>
+        <transport>hwbinder</transport>
+        <version>1.6</version>
+        <interface>
+            <name>ICameraProvider</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tests/testdata/vendorapex/vintf_fragment_wifi_service.xml b/tests/testdata/vendorapex/vintf_fragment_wifi_service.xml
new file mode 100644
index 0000000..8716929
--- /dev/null
+++ b/tests/testdata/vendorapex/vintf_fragment_wifi_service.xml
@@ -0,0 +1,15 @@
+<!--
+This file is just a copy of hardware/interfaces/wifi/1.6/default/android.hardware.wifi@1.0-service.xml.
+Attempts to install a valid vendor apex that includes this file for a vintf fragment should pass.
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.wifi</name>
+        <transport>hwbinder</transport>
+        <version>1.6</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/tests/testdata/vendorapex/wifi_file_contexts b/tests/testdata/vendorapex/wifi_file_contexts
new file mode 100644
index 0000000..04e8a62
--- /dev/null
+++ b/tests/testdata/vendorapex/wifi_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.wifi@1.0-service     u:object_r:hal_wifi_default_exec:s0
diff --git a/tests/testdata/vendorapex/wifi_manifest_rebootless.json b/tests/testdata/vendorapex/wifi_manifest_rebootless.json
new file mode 100644
index 0000000..bfa46d4
--- /dev/null
+++ b/tests/testdata/vendorapex/wifi_manifest_rebootless.json
@@ -0,0 +1,5 @@
+{
+  "name": "com.android.hardware.wifi",
+  "supportsRebootlessUpdate": true,
+  "version": 1
+}