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