1 /* 2 * Copyright (C) 2011 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.tradefed.targetprep; 18 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.config.Option.Importance; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.device.CollectingOutputReceiver; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.util.AbiFormatter; 28 import com.google.common.annotations.VisibleForTesting; 29 30 import java.io.File; 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.List; 34 import java.util.concurrent.TimeUnit; 35 36 /** 37 * A {@link ITargetPreparer} that installs one or more apks located on the filesystem. 38 * 39 * <p>This class should only be used for installing apks from the filesystem when all versions of 40 * the test rely on the apk being on the filesystem. For tests which use {@link TestAppInstallSetup} 41 * to install apks from the tests zip file, use {@code --alt-dir} to specify an alternate directory 42 * on the filesystem containing the apk for other test configurations (for example, local runs where 43 * the tests zip file is not present). 44 */ 45 @OptionClass(alias = "install-apk") 46 public class InstallApkSetup extends BaseTargetPreparer { 47 48 @Option(name = "apk-path", description = 49 "the filesystem path of the apk to install. Can be repeated.", 50 importance = Importance.IF_UNSET) 51 private Collection<File> mApkPaths = new ArrayList<File>(); 52 53 @Option(name = AbiFormatter.FORCE_ABI_STRING, 54 description = AbiFormatter.FORCE_ABI_DESCRIPTION, 55 importance = Importance.IF_UNSET) 56 private String mForceAbi = null; 57 58 @Option(name = "install-arg", 59 description = "Additional arguments to be passed to install command, " 60 + "including leading dash, e.g. \"-d\"") 61 private Collection<String> mInstallArgs = new ArrayList<>(); 62 63 @Option( 64 name = "force-queryable", 65 description = "Whether apks should be installed as force queryable.") 66 private Boolean mForceQueryable = null; 67 68 @Option(name = "post-install-cmd", description = 69 "optional post-install adb shell commands; can be repeated.") 70 private List<String> mPostInstallCmds = new ArrayList<>(); 71 72 @Option(name = "post-install-cmd-timeout", description = 73 "max time allowed in ms for a post-install adb shell command." + 74 "DeviceUnresponsiveException will be thrown if it is timed out.") 75 private long mPostInstallCmdTimeout = 2 * 60 * 1000; // default to 2 minutes 76 77 @Option(name = "throw-if-install-fail", description = 78 "Throw exception if the APK installation failed due to any reason.") 79 private boolean mThrowIfInstallFail = false; 80 81 /** 82 * {@inheritDoc} 83 */ 84 @Override setUp(ITestDevice device, IBuildInfo buildInfo)85 public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, 86 BuildError, DeviceNotAvailableException { 87 for (File apk : mApkPaths) { 88 if (!apk.exists()) { 89 throw new TargetSetupError(String.format("%s does not exist", 90 apk.getAbsolutePath()), device.getDeviceDescriptor()); 91 } 92 CLog.i("Installing %s on %s", apk.getName(), device.getSerialNumber()); 93 if (mForceAbi != null) { 94 String abi = AbiFormatter.getDefaultAbi(device, mForceAbi); 95 if (abi != null) { 96 mInstallArgs.add(String.format("--abi %s", abi)); 97 } 98 } 99 if (mForceQueryable == null) { 100 // Do not add --force-queryable if the device api level >= 34. Ideally, 101 // checkApiLevelAgainstNextRelease(34) should only return true for api 34 devices. 102 // But, it also returns true for branches like the tm-xx-plus-aosp. Adding another 103 // condition ro.build.id==TM to handle this special case. 104 mForceQueryable = 105 !device.checkApiLevelAgainstNextRelease(34) 106 || "TM".equals(device.getBuildAlias()); 107 } 108 if (mForceQueryable && device.isAppEnumerationSupported() 109 && !mInstallArgs.contains("--force-queryable")) { 110 mInstallArgs.add("--force-queryable"); 111 } 112 String result = device.installPackage(apk, true, mInstallArgs.toArray(new String[]{})); 113 if (result != null) { 114 if (mThrowIfInstallFail) { 115 throw new TargetSetupError(String.format( 116 "Stopping test: failed to install %s on device %s. Reason: %s", 117 apk.getAbsolutePath(), device.getSerialNumber(), result), 118 device.getDeviceDescriptor()); 119 } 120 CLog.e( 121 "Failed to install %s on device %s. Reason: %s", 122 apk.getAbsolutePath(), device.getSerialNumber(), result); 123 } 124 } 125 126 if (mPostInstallCmds != null && !mPostInstallCmds.isEmpty()) { 127 for (String cmd : mPostInstallCmds) { 128 // If the command had any output, the executeShellCommand method will log it at the 129 // VERBOSE level; so no need to do any logging from here. 130 CLog.d("About to run setup command on device %s: %s", 131 device.getSerialNumber(), cmd); 132 device.executeShellCommand(cmd, new CollectingOutputReceiver(), 133 mPostInstallCmdTimeout, TimeUnit.MILLISECONDS, 1); 134 } 135 } 136 } 137 getApkPaths()138 protected Collection<File> getApkPaths() { 139 return mApkPaths; 140 } 141 142 /** 143 * Sets APK paths. Exposed for testing. 144 */ 145 @VisibleForTesting setApkPaths(Collection<File> paths)146 public void setApkPaths(Collection<File> paths) { 147 mApkPaths = paths; 148 } 149 150 /** 151 * Set throw if install fail. Exposed for testing. 152 */ 153 @VisibleForTesting setThrowIfInstallFail(boolean throwIfInstallFail)154 public void setThrowIfInstallFail(boolean throwIfInstallFail) { 155 mThrowIfInstallFail = throwIfInstallFail; 156 } 157 } 158