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 package com.android.tradefed.device; 17 18 import com.android.ddmlib.Log.LogLevel; 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.util.ArrayUtil; 21 22 import java.io.File; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.HashSet; 26 import java.util.List; 27 import java.util.Set; 28 29 /** 30 * Container for {@link ITestDevice} {@link Option}s 31 */ 32 public class TestDeviceOptions { 33 34 public enum InstanceType { 35 /** A device that we remotely access via ssh and adb connect */ 36 GCE, 37 REMOTE_AVD, 38 /** 39 * A remote device inside an emulator that we access via ssh to the instance hosting the 40 * emulator then adb connect. 41 */ 42 CUTTLEFISH, 43 REMOTE_NESTED_AVD, 44 /** An android emulator. */ 45 EMULATOR, 46 /** Chrome OS VM (betty) */ 47 CHEEPS, 48 } 49 50 public static final int DEFAULT_ADB_PORT = 5555; 51 public static final String INSTANCE_TYPE_OPTION = "instance-type"; 52 53 /** Do not provide a setter method for that Option as it might be misused. */ 54 @Option(name = "enable-root", description = "enable adb root on boot.") 55 private boolean mEnableAdbRoot = true; 56 57 @Option(name = "disable-keyguard", 58 description = "attempt to disable keyguard once boot is complete.") 59 private boolean mDisableKeyguard = true; 60 61 @Option(name = "enable-logcat", description = 62 "Enable background logcat capture when invocation is running.") 63 private boolean mEnableLogcat = true; 64 65 @Option(name = "max-tmp-logcat-file", description = 66 "The maximum size of tmp logcat data to retain, in bytes. " + 67 "Only used if --enable-logcat is set") 68 private long mMaxLogcatDataSize = 20 * 1024 * 1024; 69 70 @Option(name = "logcat-options", description = 71 "Options to be passed down to logcat command, if unspecified, \"-v threadtime\" will " + 72 "be used. Only used if --enable-logcat is set") 73 private String mLogcatOptions = null; 74 75 @Option(name = "fastboot-timeout", description = 76 "time in ms to wait for a device to boot into fastboot.") 77 private int mFastbootTimeout = 1 * 60 * 1000; 78 79 @Option(name = "adb-recovery-timeout", description = 80 "time in ms to wait for a device to boot into recovery.") 81 private int mAdbRecoveryTimeout = 1 * 60 * 1000; 82 83 @Option(name = "reboot-timeout", description = 84 "time in ms to wait for a device to reboot to full system.") 85 private int mRebootTimeout = 2 * 60 * 1000; 86 87 @Option(name = "use-fastboot-erase", description = 88 "use fastboot erase instead of fastboot format to wipe partitions") 89 private boolean mUseFastbootErase = false; 90 91 @Option(name = "unencrypt-reboot-timeout", description = "time in ms to wait for the device to " 92 + "format the filesystem and reboot after unencryption") 93 private int mUnencryptRebootTimeout = 0; 94 95 @Option(name = "online-timeout", description = "default time in ms to wait for the device to " 96 + "be visible on adb.", isTimeVal = true) 97 private long mOnlineTimeout = 1 * 60 * 1000; 98 99 @Option(name = "available-timeout", description = "default time in ms to wait for the device " 100 + "to be available aka fully boot.") 101 private long mAvailableTimeout = 6 * 60 * 1000; 102 103 @Option(name = "conn-check-url", 104 description = "default URL to be used for connectivity checks.") 105 private String mConnCheckUrl = "http://www.google.com"; 106 107 @Option(name = "wifi-attempts", 108 description = "default number of attempts to connect to wifi network.") 109 private int mWifiAttempts = 5; 110 111 @Option(name = "wifi-retry-wait-time", 112 description = "the base wait time in ms between wifi connect retries. " 113 + "The actual wait time would be a multiple of this value.") 114 private int mWifiRetryWaitTime = 60 * 1000; 115 116 @Option( 117 name = "max-wifi-connect-time", 118 isTimeVal = true, 119 description = "the maximum amount of time to attempt to connect to wifi." 120 ) 121 private long mMaxWifiConnectTime = 10 * 60 * 1000; 122 123 @Option(name = "wifi-exponential-retry", 124 description = "Change the wifi connection retry strategy from a linear wait time into" 125 + " a binary exponential back-offs when retrying.") 126 private boolean mWifiExpoRetryEnabled = true; 127 128 @Option(name = "wifiutil-apk-path", description = "path to the wifiutil APK file") 129 private String mWifiUtilAPKPath = null; 130 131 @Option(name = "post-boot-command", 132 description = "shell command to run after reboots during invocation") 133 private List<String> mPostBootCommands = new ArrayList<String>(); 134 135 @Option(name = "disable-reboot", 136 description = "disables device reboots globally, making them no-ops") 137 private boolean mDisableReboot = false; 138 139 @Option(name = "cutoff-battery", description = 140 "the minimum battery level required to continue the invocation. Scale: 0-100") 141 private Integer mCutoffBattery = null; 142 143 @Option( 144 name = "use-content-provider", 145 description = 146 "Allow to disable the use of the content provider at the device level. " 147 + "This results in falling back to standard adb push/pull." 148 ) 149 private boolean mUseContentProvider = true; 150 151 // ====================== Options Related to Virtual Devices ====================== 152 @Option( 153 name = INSTANCE_TYPE_OPTION, 154 description = "The type of virtual device instance to create") 155 private InstanceType mInstanceType = InstanceType.GCE; 156 157 @Option( 158 name = "gce-boot-timeout", 159 description = "timeout to wait in ms for GCE to be online.", 160 isTimeVal = true) 161 private long mGceCmdTimeout = 30 * 60 * 1000; // 30 minutes. 162 163 @Option( 164 name = "allow-gce-boot-timeout-override", 165 description = 166 "Acloud can take boot-timeout as an arg already, this flag allows to use " 167 + "the Acloud value as a basis instead of the gce-boot-timeout option.") 168 private boolean mAllowGceCmdTimeoutOverride = false; 169 170 @Option(name = "gce-driver-path", description = "path of the binary to launch GCE devices") 171 private File mAvdDriverBinary = null; 172 173 @Option( 174 name = "gce-driver-config-path", 175 description = "path of the config to use to launch GCE devices.") 176 private File mAvdConfigFile = null; 177 178 @Option( 179 name = "gce-driver-service-account-json-key-path", 180 description = "path to the service account json key location.") 181 private File mJsonKeyFile = null; 182 183 @Option( 184 name = "gce-private-key-path", 185 description = "path to the ssh key private key location.") 186 private File mSshPrivateKeyPath = new File("~/.ssh/id_rsa"); 187 188 @Option(name = "gce-driver-log-level", description = "Log level for gce driver") 189 private LogLevel mGceDriverLogLevel = LogLevel.DEBUG; 190 191 @Option( 192 name = "gce-driver-param", 193 description = "Additional args to pass to gce driver as parameters." 194 ) 195 private List<String> mGceDriverParams = new ArrayList<>(); 196 197 @Deprecated 198 @Option( 199 name = "gce-driver-build-id-param", 200 description = 201 "The parameter to be paired with " 202 + "build id from build info when passed down to gce driver" 203 ) 204 private String mGceDriverBuildIdParam = "build_id"; 205 206 @Option(name = "gce-account", description = "email account to use with GCE driver.") 207 private String mGceAccount = null; 208 209 @Option( 210 name = "max-gce-attempt", 211 description = "Maximum number of attempts to start Gce before throwing an exception." 212 ) 213 private int mGceMaxAttempt = 1; 214 215 @Option( 216 name = "skip-gce-teardown", 217 description = 218 "Whether or not to skip the GCE tear down. Skipping tear down will " 219 + "result in the instance being left.") 220 private boolean mSkipTearDown = false; 221 222 @Option( 223 name = "wait-gce-teardown", 224 description = "Whether or not to block on gce teardown before proceeding.") 225 private boolean mWaitForGceTearDown = false; 226 227 @Option( 228 name = "instance-user", 229 description = 230 "The account to be used to interact with the " 231 + "outer layer of the GCE VM, e.g. to SSH in") 232 private String mInstanceUser = "root"; 233 234 @Option( 235 name = "remote-adb-port", 236 description = "The port on remote instance where the adb " + "server listens to.") 237 private int mRemoteAdbPort = DEFAULT_ADB_PORT; 238 239 @Option( 240 name = "base-host-image", 241 description = "The base image to be used for the GCE VM to host emulator.") 242 private String mBaseImage = null; 243 244 @Option( 245 name = "remote-fetch-file-pattern", 246 description = 247 "Only for remote VM devices. Allows to specify patterns to fetch file on the " 248 + "remote VM via scp. Pattern must follow the scp notations." 249 ) 250 private Set<String> mRemoteFetchFilePattern = new HashSet<>(); 251 252 @Option(name = "cros-user", description = "(CHEEPS ONLY) Account to log in to Chrome OS with.") 253 private String mCrosUser = null; 254 255 @Option( 256 name = "cros-password", 257 description = 258 "(CHEEPS ONLY) Password to log in to Chrome OS with. Only used if cros-user " 259 + "is specified." 260 ) 261 private String mCrosPassword = null; 262 263 // END ====================== Options Related to Virtual Devices ====================== 264 265 // Option related to Remote Device only 266 267 public static final String REMOTE_TF_VERSION_OPTION = "remote-tf-version"; 268 269 @Option( 270 name = REMOTE_TF_VERSION_OPTION, 271 description = 272 "The TF to push to the remote VM to drive the invocation. If null, current TF " 273 + "will be pushed." 274 ) 275 private File mRemoteTFVersion = null; 276 277 /** Check whether adb root should be enabled on boot for this device */ isEnableAdbRoot()278 public boolean isEnableAdbRoot() { 279 return mEnableAdbRoot; 280 } 281 282 /** 283 * Check whether or not we should attempt to disable the keyguard once boot has completed 284 */ isDisableKeyguard()285 public boolean isDisableKeyguard() { 286 return mDisableKeyguard; 287 } 288 289 /** 290 * Set whether or not we should attempt to disable the keyguard once boot has completed 291 */ setDisableKeyguard(boolean disableKeyguard)292 public void setDisableKeyguard(boolean disableKeyguard) { 293 mDisableKeyguard = disableKeyguard; 294 } 295 296 /** 297 * Get the approximate maximum size of a tmp logcat data to retain, in bytes. 298 */ getMaxLogcatDataSize()299 public long getMaxLogcatDataSize() { 300 return mMaxLogcatDataSize; 301 } 302 303 /** 304 * Set the approximate maximum size of a tmp logcat to retain, in bytes 305 */ setMaxLogcatDataSize(long maxLogcatDataSize)306 public void setMaxLogcatDataSize(long maxLogcatDataSize) { 307 mMaxLogcatDataSize = maxLogcatDataSize; 308 } 309 310 /** 311 * @return the timeout to boot into fastboot mode in msecs. 312 */ getFastbootTimeout()313 public int getFastbootTimeout() { 314 return mFastbootTimeout; 315 } 316 317 /** 318 * @param fastbootTimeout the timout in msecs to boot into fastboot mode. 319 */ setFastbootTimeout(int fastbootTimeout)320 public void setFastbootTimeout(int fastbootTimeout) { 321 mFastbootTimeout = fastbootTimeout; 322 } 323 324 /** 325 * @return the timeout in msecs to boot into recovery mode. 326 */ getAdbRecoveryTimeout()327 public int getAdbRecoveryTimeout() { 328 return mAdbRecoveryTimeout; 329 } 330 331 /** 332 * @param adbRecoveryTimeout the timeout in msecs to boot into recovery mode. 333 */ setAdbRecoveryTimeout(int adbRecoveryTimeout)334 public void setAdbRecoveryTimeout(int adbRecoveryTimeout) { 335 mAdbRecoveryTimeout = adbRecoveryTimeout; 336 } 337 338 /** 339 * @return the timeout in msecs for the full system boot. 340 */ getRebootTimeout()341 public int getRebootTimeout() { 342 return mRebootTimeout; 343 } 344 345 /** 346 * @param rebootTimeout the timeout in msecs for the system to fully boot. 347 */ setRebootTimeout(int rebootTimeout)348 public void setRebootTimeout(int rebootTimeout) { 349 mRebootTimeout = rebootTimeout; 350 } 351 352 /** 353 * @return whether to use fastboot erase instead of fastboot format to wipe partitions. 354 */ getUseFastbootErase()355 public boolean getUseFastbootErase() { 356 return mUseFastbootErase; 357 } 358 359 /** 360 * @param useFastbootErase whether to use fastboot erase instead of fastboot format to wipe 361 * partitions. 362 */ setUseFastbootErase(boolean useFastbootErase)363 public void setUseFastbootErase(boolean useFastbootErase) { 364 mUseFastbootErase = useFastbootErase; 365 } 366 367 /** 368 * @return the timeout in msecs for the filesystem to be formatted and the device to reboot 369 * after unencryption. 370 */ getUnencryptRebootTimeout()371 public int getUnencryptRebootTimeout() { 372 return mUnencryptRebootTimeout; 373 } 374 375 /** 376 * @param unencryptRebootTimeout the timeout in msecs for the filesystem to be formatted and 377 * the device to reboot after unencryption. 378 */ setUnencryptRebootTimeout(int unencryptRebootTimeout)379 public void setUnencryptRebootTimeout(int unencryptRebootTimeout) { 380 mUnencryptRebootTimeout = unencryptRebootTimeout; 381 } 382 383 /** 384 * @return the default time in ms to to wait for a device to be online. 385 */ getOnlineTimeout()386 public long getOnlineTimeout() { 387 return mOnlineTimeout; 388 } 389 setOnlineTimeout(long onlineTimeout)390 public void setOnlineTimeout(long onlineTimeout) { 391 mOnlineTimeout = onlineTimeout; 392 } 393 394 /** 395 * @return the default time in ms to to wait for a device to be available. 396 */ getAvailableTimeout()397 public long getAvailableTimeout() { 398 return mAvailableTimeout; 399 } 400 401 /** 402 * @return the default URL to be used for connectivity tests. 403 */ getConnCheckUrl()404 public String getConnCheckUrl() { 405 return mConnCheckUrl; 406 } 407 setConnCheckUrl(String url)408 public void setConnCheckUrl(String url) { 409 mConnCheckUrl = url; 410 } 411 412 /** 413 * @return true if background logcat capture is enabled 414 */ isLogcatCaptureEnabled()415 public boolean isLogcatCaptureEnabled() { 416 return mEnableLogcat; 417 } 418 419 /** 420 * @return the default number of attempts to connect to wifi network. 421 */ getWifiAttempts()422 public int getWifiAttempts() { 423 return mWifiAttempts; 424 } 425 setWifiAttempts(int wifiAttempts)426 public void setWifiAttempts(int wifiAttempts) { 427 mWifiAttempts = wifiAttempts; 428 } 429 430 /** 431 * @return the base wait time between wifi connect retries. 432 */ getWifiRetryWaitTime()433 public int getWifiRetryWaitTime() { 434 return mWifiRetryWaitTime; 435 } 436 437 /** @return the maximum time to attempt to connect to wifi. */ getMaxWifiConnectTime()438 public long getMaxWifiConnectTime() { 439 return mMaxWifiConnectTime; 440 } 441 442 /** 443 * @return a list of shell commands to run after reboots. 444 */ getPostBootCommands()445 public List<String> getPostBootCommands() { 446 return mPostBootCommands; 447 } 448 449 /** 450 * @return the minimum battery level to continue the invocation. 451 */ getCutoffBattery()452 public Integer getCutoffBattery() { 453 return mCutoffBattery; 454 } 455 456 /** 457 * set the minimum battery level to continue the invocation. 458 */ setCutoffBattery(int cutoffBattery)459 public void setCutoffBattery(int cutoffBattery) { 460 if (cutoffBattery < 0 || cutoffBattery > 100) { 461 // Prevent impossible value. 462 throw new RuntimeException(String.format("Battery cutoff wasn't changed," 463 + "the value %s isn't within possible range (0-100).", cutoffBattery)); 464 } 465 mCutoffBattery = cutoffBattery; 466 } 467 468 /** 469 * @return the configured logcat options 470 */ getLogcatOptions()471 public String getLogcatOptions() { 472 return mLogcatOptions; 473 } 474 475 /** 476 * Set the options to be passed down to logcat 477 */ setLogcatOptions(String logcatOptions)478 public void setLogcatOptions(String logcatOptions) { 479 mLogcatOptions = logcatOptions; 480 } 481 482 /** 483 * @return if device reboot should be disabled 484 */ shouldDisableReboot()485 public boolean shouldDisableReboot() { 486 return mDisableReboot; 487 } 488 489 /** 490 * @return if the exponential retry strategy should be used. 491 */ isWifiExpoRetryEnabled()492 public boolean isWifiExpoRetryEnabled() { 493 return mWifiExpoRetryEnabled; 494 } 495 496 /** @return the wifiutil apk path */ getWifiUtilAPKPath()497 public String getWifiUtilAPKPath() { 498 return mWifiUtilAPKPath; 499 } 500 501 /** Returns the instance type of virtual device that should be created */ getInstanceType()502 public InstanceType getInstanceType() { 503 return mInstanceType; 504 } 505 506 /** Returns whether or not the Tradefed content provider can be used to push/pull files. */ shouldUseContentProvider()507 public boolean shouldUseContentProvider() { 508 return mUseContentProvider; 509 } 510 511 // =========================== Getter and Setter for Virtual Devices 512 /** Return the Gce Avd timeout for the instance to come online. */ getGceCmdTimeout()513 public long getGceCmdTimeout() { 514 return mGceCmdTimeout; 515 } 516 517 /** Set the Gce Avd timeout for the instance to come online. */ setGceCmdTimeout(long gceCmdTimeout)518 public void setGceCmdTimeout(long gceCmdTimeout) { 519 mGceCmdTimeout = gceCmdTimeout; 520 } 521 522 /** Returns whether or not we should rely on the boot-timeout args from acloud if present. */ allowGceCmdTimeoutOverride()523 public boolean allowGceCmdTimeoutOverride() { 524 return mAllowGceCmdTimeoutOverride; 525 } 526 527 /** Return the path to the binary to start the Gce Avd instance. */ getAvdDriverBinary()528 public File getAvdDriverBinary() { 529 return mAvdDriverBinary; 530 } 531 532 /** Set the path to the binary to start the Gce Avd instance. */ setAvdDriverBinary(File avdDriverBinary)533 public void setAvdDriverBinary(File avdDriverBinary) { 534 mAvdDriverBinary = avdDriverBinary; 535 } 536 537 /** Return the Gce Avd config file to start the instance. */ getAvdConfigFile()538 public File getAvdConfigFile() { 539 return mAvdConfigFile; 540 } 541 542 /** Set the Gce Avd config file to start the instance. */ setAvdConfigFile(File avdConfigFile)543 public void setAvdConfigFile(File avdConfigFile) { 544 mAvdConfigFile = avdConfigFile; 545 } 546 547 /** @return the service account json key file. */ getServiceAccountJsonKeyFile()548 public File getServiceAccountJsonKeyFile() { 549 return mJsonKeyFile; 550 } 551 552 /** 553 * Set the service account json key file. 554 * 555 * @param jsonKeyFile the key file. 556 */ setServiceAccountJsonKeyFile(File jsonKeyFile)557 public void setServiceAccountJsonKeyFile(File jsonKeyFile) { 558 mJsonKeyFile = jsonKeyFile; 559 } 560 561 /** Return the path of the ssh key to use for operations with the Gce Avd instance. */ getSshPrivateKeyPath()562 public File getSshPrivateKeyPath() { 563 return mSshPrivateKeyPath; 564 } 565 566 /** Set the path of the ssh key to use for operations with the Gce Avd instance. */ setSshPrivateKeyPath(File sshPrivateKeyPath)567 public void setSshPrivateKeyPath(File sshPrivateKeyPath) { 568 mSshPrivateKeyPath = sshPrivateKeyPath; 569 } 570 571 /** Return the log level of the Gce Avd driver. */ getGceDriverLogLevel()572 public LogLevel getGceDriverLogLevel() { 573 return mGceDriverLogLevel; 574 } 575 576 /** Set the log level of the Gce Avd driver. */ setGceDriverLogLevel(LogLevel mGceDriverLogLevel)577 public void setGceDriverLogLevel(LogLevel mGceDriverLogLevel) { 578 this.mGceDriverLogLevel = mGceDriverLogLevel; 579 } 580 581 /** Return the additional GCE driver parameters provided via option */ getGceDriverParams()582 public List<String> getGceDriverParams() { 583 return mGceDriverParams; 584 } 585 586 /** Add a param to the gce driver params. */ addGceDriverParams(String param)587 public void addGceDriverParams(String param) { 588 mGceDriverParams.add(param); 589 } 590 591 /** Set the GCE driver parameter that should be paired with the build id from build info */ setGceDriverBuildIdParam(String gceDriverBuildIdParam)592 public void setGceDriverBuildIdParam(String gceDriverBuildIdParam) { 593 mGceDriverBuildIdParam = gceDriverBuildIdParam; 594 } 595 596 /** Return the GCE driver parameter that should be paired with the build id from build info */ getGceDriverBuildIdParam()597 public String getGceDriverBuildIdParam() { 598 return mGceDriverBuildIdParam; 599 } 600 601 /** Return the gce email account to use with the driver */ getGceAccount()602 public String getGceAccount() { 603 return mGceAccount; 604 } 605 606 /** Return the max number of attempts to start a gce device */ getGceMaxAttempt()607 public int getGceMaxAttempt() { 608 if (mGceMaxAttempt < 1) { 609 throw new RuntimeException("--max-gce-attempt cannot be bellow 1 attempt."); 610 } 611 return mGceMaxAttempt; 612 } 613 614 /** Set the max number of attempts to start a gce device */ setGceMaxAttempt(int gceMaxAttempt)615 public void setGceMaxAttempt(int gceMaxAttempt) { 616 mGceMaxAttempt = gceMaxAttempt; 617 } 618 619 /** Returns true if GCE tear down should be skipped. False otherwise. */ shouldSkipTearDown()620 public boolean shouldSkipTearDown() { 621 return mSkipTearDown; 622 } 623 624 /** Returns true if we should block on GCE tear down completion before proceeding. */ waitForGceTearDown()625 public boolean waitForGceTearDown() { 626 return mWaitForGceTearDown; 627 } 628 629 /** Returns the instance type of GCE virtual device that should be created */ getInstanceUser()630 public String getInstanceUser() { 631 return mInstanceUser; 632 } 633 634 /** Returns the remote port in instance that the adb server listens to */ getRemoteAdbPort()635 public int getRemoteAdbPort() { 636 return mRemoteAdbPort; 637 } 638 639 /** Returns the base image name to be used for the current instance */ getBaseImage()640 public String getBaseImage() { 641 return mBaseImage; 642 } 643 644 /** Returns the list of pattern to attempt to fetch via scp. */ getRemoteFetchFilePattern()645 public Set<String> getRemoteFetchFilePattern() { 646 return mRemoteFetchFilePattern; 647 } 648 649 /** Returns the Chrome OS User to log in as. */ getCrosUser()650 public String getCrosUser() { 651 return mCrosUser; 652 } 653 654 /** Returns the password to log in to Chrome OS with. */ getCrosPassword()655 public String getCrosPassword() { 656 return mCrosPassword; 657 } 658 659 /** The file pointing to the directory of the Tradefed version to be pushed to the remote. */ getRemoteTf()660 public File getRemoteTf() { 661 return mRemoteTFVersion; 662 } 663 getCreateCommandByInstanceType(InstanceType type)664 public static String getCreateCommandByInstanceType(InstanceType type) { 665 switch (type) { 666 case CHEEPS: 667 case GCE: 668 case REMOTE_AVD: 669 return "create"; 670 case CUTTLEFISH: 671 case REMOTE_NESTED_AVD: 672 return "create_cf"; 673 case EMULATOR: 674 return "create_gf"; 675 } 676 throw new RuntimeException("Unexpected InstanceType: " + type); 677 } 678 getExtraParamsByInstanceType(InstanceType type, String baseImage)679 public static List<String> getExtraParamsByInstanceType(InstanceType type, String baseImage) { 680 if (InstanceType.EMULATOR.equals(type)) { 681 // TODO(b/119440413) remove when base image can be passed via extra gce driver params 682 List<String> params = ArrayUtil.list(); 683 if (baseImage != null) { 684 params.add("--base_image"); 685 params.add(baseImage); 686 } 687 return params; 688 } 689 return Collections.emptyList(); 690 } 691 } 692