1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tests.apex.host; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assume.assumeTrue; 23 24 import android.cts.install.lib.host.InstallUtilsHost; 25 import android.platform.test.annotations.LargeTest; 26 27 import com.android.apex.ApexInfo; 28 import com.android.apex.XmlParser; 29 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 30 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 31 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 32 import com.android.tradefed.util.CommandResult; 33 import com.android.tradefed.util.CommandStatus; 34 35 import org.junit.After; 36 import org.junit.Before; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import static java.util.stream.Collectors.toList; 41 42 import java.io.File; 43 import java.io.FileInputStream; 44 import java.nio.file.Paths; 45 import java.util.List; 46 47 @RunWith(DeviceJUnit4ClassRunner.class) 48 public class VendorApexTests extends BaseHostJUnit4Test { 49 50 private static final String TAG = "VendorApexTests"; 51 private static final String APEX_PACKAGE_NAME = "com.android.apex.vendor.foo"; 52 53 private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); 54 runPhase(String phase)55 private void runPhase(String phase) throws Exception { 56 assertThat(runDeviceTests("com.android.tests.vendorapex.app", 57 "com.android.tests.apex.app.VendorApexTests", 58 phase)).isTrue(); 59 } 60 61 @Before setUp()62 public void setUp() throws Exception { 63 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 64 65 // TODO(b/280155297) workaround to ensure RW. When partition.vendor.verified is still 66 // set to something, then the device should be remount-ed again. 67 getDevice().remountVendorWritable(); 68 String verity = getDevice().getProperty("partition.vendor.verified"); 69 if (verity != null && !verity.isEmpty()) { 70 getDevice().remountVendorWritable(); 71 } 72 } 73 74 @After tearDown()75 public void tearDown() throws Exception { 76 deleteFiles("/vendor/apex/" + APEX_PACKAGE_NAME + "*apex", 77 "/data/apex/active/" + APEX_PACKAGE_NAME + "*apex"); 78 } 79 80 @Test 81 @LargeTest testRebootlessUpdate()82 public void testRebootlessUpdate() throws Exception { 83 pushPreinstalledApex("com.android.apex.vendor.foo.apex"); 84 85 runPhase("testRebootlessUpdate"); 86 } 87 88 @Test 89 @LargeTest testGenerateLinkerConfigurationOnUpdate()90 public void testGenerateLinkerConfigurationOnUpdate() throws Exception { 91 pushPreinstalledApex("com.android.apex.vendor.foo.apex"); 92 runPhase("testGenerateLinkerConfigurationOnUpdate"); 93 } 94 95 @Test 96 @LargeTest testInstallAbortsWhenVndkVersionMismatches()97 public void testInstallAbortsWhenVndkVersionMismatches() throws Exception { 98 pushPreinstalledApex("com.android.apex.vendor.foo.apex"); 99 runPhase("testInstallAbortsWhenVndkVersionMismatches"); 100 runPhase("testInstallAbortsWhenVndkVersionMismatches_Staged"); 101 } 102 103 @Test 104 @LargeTest testApexAllReady()105 public void testApexAllReady() throws Exception { 106 pushPreinstalledApex("com.android.apex.vendor.foo.apex.all.ready.apex"); 107 assertThat(getDevice().getProperty("vendor.test.apex.all.ready")).isEqualTo("triggered"); 108 } 109 110 @Test 111 @LargeTest testRestartServiceAfterRebootlessUpdate()112 public void testRestartServiceAfterRebootlessUpdate() throws Exception { 113 pushPreinstalledApex("com.android.apex.vendor.foo.v1_with_service.apex"); 114 runPhase("testRestartServiceAfterRebootlessUpdate"); 115 } 116 117 @Test 118 @LargeTest testVendorBootstrapApex()119 public void testVendorBootstrapApex() throws Exception { 120 pushPreinstalledApex("com.android.apex.vendor.foo.bootstrap.apex"); 121 122 // Now there should be "com.android.apex.vendor.foo" activated as an 123 // bootstrap apex, listed in apex-info-list in the bootstrap mount namespace. 124 try (FileInputStream fis = new FileInputStream( 125 getDevice().pullFile("/bootstrap-apex/apex-info-list.xml"))) { 126 List<String> names = XmlParser.readApexInfoList(fis) 127 .getApexInfo() 128 .stream() 129 .map(ApexInfo::getModuleName) 130 .collect(toList()); 131 assertThat(names).contains("com.android.apex.vendor.foo"); 132 } 133 134 // And also the `early_hal` service in the apex (apex_vendor_foo) should 135 // be started in the bootstrap mount namespace. 136 assertThat(getMountNamespaceFor("$(pidof apex_vendor_foo)")) 137 .isEqualTo(getMountNamespaceFor("$(pidof vold)")); 138 } 139 getMountNamespaceFor(String proc)140 private String getMountNamespaceFor(String proc) throws Exception { 141 CommandResult result = 142 getDevice().executeShellV2Command("readlink /proc/" + proc + "/ns/mnt"); 143 if (result.getStatus() != CommandStatus.SUCCESS) { 144 throw new RuntimeException("failed to read namespace for " + proc); 145 } 146 return result.getStdout().trim(); 147 } 148 pushPreinstalledApex(String fileName)149 private void pushPreinstalledApex(String fileName) throws Exception { 150 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); 151 final File apex = buildHelper.getTestFile(fileName); 152 assertTrue(getDevice().pushFile(apex, Paths.get("/vendor/apex", fileName).toString())); 153 getDevice().reboot(); 154 } 155 156 /** 157 * Deletes files and reboots the device if necessary. 158 * @param files the paths of files which might contain wildcards 159 */ deleteFiles(String... files)160 private void deleteFiles(String... files) throws Exception { 161 boolean found = false; 162 for (String file : files) { 163 CommandResult result = getDevice().executeShellV2Command("ls " + file); 164 if (result.getStatus() == CommandStatus.SUCCESS) { 165 found = true; 166 break; 167 } 168 } 169 170 if (found) { 171 getDevice().remountVendorWritable(); 172 for (String file : files) { 173 getDevice().executeShellCommand("rm -rf " + file); 174 } 175 getDevice().reboot(); 176 } 177 } 178 } 179