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.modules.testing.utils; 18 19 import static org.junit.Assert.assertTrue; 20 21 import android.cts.install.lib.host.InstallUtilsHost; 22 23 import com.android.tradefed.device.ITestDevice; 24 import com.android.tradefed.invoker.TestInformation; 25 26 import com.android.internal.util.test.SystemPreparer; 27 import com.android.internal.util.test.SystemPreparer.DeviceProvider; 28 import com.android.internal.util.test.SystemPreparer.RebootStrategy; 29 30 import org.junit.rules.TemporaryFolder; 31 32 import java.io.File; 33 34 /** 35 * Utility to help installing and updating apexes in tests as well as cleaning them up when the test 36 * finishes. 37 * 38 * <p>Install vs updating: you can update any apex preinstalled in the system image as long as your 39 * apex has a higher version than the one currently installed using 40 * {@link #installApexAndReboot}. If you want to use an apex not preinstalled you can call 41 * {@link #pushApexAndReboot}. After you push one (non-preinstalled) apex, you can update it using 42 * either method. 43 * 44 * <p>In order to revert the apexes that were installed or pushed, call 45 * {@link #revertChanges}. This normally happens from your {@code @AfterClass} method. 46 * 47 * <p>Requirements: the device must support apex updates and have root access via adb. 48 * 49 * <p>Example usage: 50 * 51 * <pre>{@code 52 * 53 * static private ApexInstallHelper sApexInstallHelper; 54 * 55 * @BeforeClassWithInfo 56 * public static void beforeClassWithDevice(TestInformation testInformation) 57 * throws Exception { 58 * sApexInstallHelper = new ApexInstallHelper(testInformation); 59 * sApexInstallHelper.pushApexAndReboot("test_com.android.modules.example.apex"); 60 * // assuming next line matches a preinstalled apex 61 * sApexInstallHelper.installApexAndReboot("art.apex"); 62 * } 63 * 64 * @AfterClass 65 * public static void afterClass() { 66 * sApexInstallHelper.revertChanges(); 67 * } 68 * 69 * // add your own @Test methods 70 * }</pre> 71 * 72 * <p>Additionally, the apex files that you want to update or install need to be accessible 73 * to the test: 74 * 75 * <pre>{@code 76 * 77 * java_test_host { 78 * name: "Example", 79 * data: [ 80 * ":test_com.android.modules.example", 81 * ], 82 * static_libs: [ 83 * "com.android.modules.testing.utils", 84 * ], 85 * }</pre> 86 * 87 */ 88 public class ApexInstallHelper { 89 private final TemporaryFolder mTemporaryFolder; 90 private final SystemPreparer mSystemPreparer; 91 private final InstallUtilsHost mInstallUtilsHost; 92 private final DeviceProvider mDeviceProvider; 93 private boolean mCalledSetUp = false; 94 ApexInstallHelper(TestInformation testInformation)95 public ApexInstallHelper(TestInformation testInformation) { 96 this(testInformation, RebootStrategy.FULL); 97 } 98 ApexInstallHelper(TestInformation testInformation, RebootStrategy rebootStrategy)99 public ApexInstallHelper(TestInformation testInformation, RebootStrategy rebootStrategy) { 100 mDeviceProvider = testInformation::getDevice; 101 mTemporaryFolder = new TemporaryFolder(); 102 mSystemPreparer = new SystemPreparer(mTemporaryFolder, rebootStrategy, 103 /*testRuleDelegate=*/null, mDeviceProvider); 104 mInstallUtilsHost = new InstallUtilsHost(testInformation); 105 } 106 setUp()107 private void setUp() throws Exception { 108 if (mCalledSetUp) { 109 return; 110 } 111 mCalledSetUp = true; 112 mTemporaryFolder.create(); 113 // mSystemPreparer doesn't expect to be called to setUp 114 115 ITestDevice device = mDeviceProvider.getDevice(); 116 if (!device.isAdbRoot()) { 117 device.enableAdbRoot(); 118 } 119 120 assertTrue("Device does not support updating APEX", 121 mInstallUtilsHost.isApexUpdateSupported()); 122 assertTrue("Device requires root", device.isAdbRoot()); 123 } 124 125 /** 126 * Installs an apex (using {@code adb push}) on the device. This can be used to install a new 127 * apex that is not preinstalled in the system image. 128 * 129 * @param apexFilename the filename of the apex to install. 130 */ pushApexAndReboot(String apexFilename)131 public void pushApexAndReboot(String apexFilename) throws Exception { 132 setUp(); 133 File apexFile = mInstallUtilsHost.getTestFile(apexFilename); 134 mSystemPreparer.pushFile(apexFile, "/system/apex/" + apexFilename); 135 mSystemPreparer.reboot(); 136 } 137 138 /** 139 * Installs an apex (using {@code adb install}) on the device. This can only be used to 140 * install a newer version of an apex that is already installed on the device (either 141 * preinstalled in the system image or previously installed using 142 * {@link #installApexAndReboot}). 143 * 144 * @param apexFilename the filename of the apex which contains the update. 145 */ installApexAndReboot(String apexFilename)146 public void installApexAndReboot(String apexFilename) throws Exception { 147 setUp(); 148 File apexFile = mInstallUtilsHost.getTestFile(apexFilename); 149 mDeviceProvider.getDevice().installPackage(apexFile, true); 150 mSystemPreparer.reboot(); 151 } 152 153 /** 154 * Calls this method to undo all installs and pushes performed by this helper when your test 155 * has finished. 156 * 157 * <p>This should typically be called from your {@code @AfterClass} method. 158 */ revertChanges()159 public void revertChanges() { 160 if (!mCalledSetUp) { 161 return; 162 } 163 // in line with other tests, clean up system preparer before the temporary folder 164 mSystemPreparer.after(); 165 mTemporaryFolder.delete(); 166 mCalledSetUp = false; 167 } 168 } 169