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
+}