1 /* 2 * Copyright (C) 201040 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.annotations.VisibleForTesting; 19 import com.android.ddmlib.IDevice; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.config.OptionUpdateRule; 22 import com.android.tradefed.device.DeviceManager.FastbootDevice; 23 import com.android.tradefed.device.cloud.VmRemoteDevice; 24 import com.android.tradefed.log.LogUtil.CLog; 25 26 import com.google.common.base.Strings; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.LinkedHashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.concurrent.ExecutionException; 37 import java.util.concurrent.Future; 38 import java.util.concurrent.TimeUnit; 39 40 /** 41 * Container for for device selection criteria. 42 */ 43 public class DeviceSelectionOptions implements IDeviceSelection { 44 45 /** The different possible types of placeholder devices supported. */ 46 public enum DeviceRequestedType { 47 /** A placeholder where no device is required to be allocated. */ 48 NULL_DEVICE(NullDevice.class), 49 /** Allocate an emulator running locally for the test. */ 50 LOCAL_EMULATOR(StubDevice.class), 51 /** Use a placeholder for a remote device nested in a virtualized environment. */ 52 GCE_DEVICE(RemoteAvdIDevice.class), 53 /** Use a placeholder for a remote device in virtualized environment. */ 54 REMOTE_DEVICE(VmRemoteDevice.class), 55 /** Allocate a virtual device running on localhost. */ 56 LOCAL_VIRTUAL_DEVICE(StubLocalAndroidVirtualDevice.class), 57 /** A real physical or virtual device already started, not a placeholder type. */ 58 EXISTING_DEVICE(IDevice.class); 59 60 private Class<?> mRequiredIDeviceClass; 61 DeviceRequestedType(Class<?> requiredIDeviceClass)62 DeviceRequestedType(Class<?> requiredIDeviceClass) { 63 mRequiredIDeviceClass = requiredIDeviceClass; 64 } 65 getRequiredClass()66 public Class<?> getRequiredClass() { 67 return mRequiredIDeviceClass; 68 } 69 } 70 71 @Option(name = "serial", shortName = 's', description = 72 "run this test on a specific device with given serial number(s).") 73 private Collection<String> mSerials = new ArrayList<String>(); 74 75 @Option(name = "exclude-serial", description = 76 "run this test on any device except those with this serial number(s).") 77 private Collection<String> mExcludeSerials = new ArrayList<String>(); 78 79 @Option(name = "product-type", description = 80 "run this test on device with this product type(s). May also filter by variant " + 81 "using product:variant.") 82 private Collection<String> mProductTypes = new ArrayList<String>(); 83 84 @Option(name = "property", description = 85 "run this test on device with this property value. " + 86 "Expected format --property <propertyname> <propertyvalue>.") 87 private Map<String, String> mPropertyMap = new HashMap<>(); 88 89 // ============================ DEVICE TYPE Related Options =============================== 90 @Option(name = "emulator", shortName = 'e', description = "force this test to run on emulator.") 91 private boolean mEmulatorRequested = false; 92 93 @Option(name = "device", shortName = 'd', description = 94 "force this test to run on a physical device, not an emulator.") 95 private boolean mDeviceRequested = false; 96 97 @Option(name = "new-emulator", description = 98 "allocate a placeholder emulator. Should be used when config intends to launch an emulator") 99 private boolean mStubEmulatorRequested = false; 100 101 @Option(name = "null-device", shortName = 'n', description = 102 "do not allocate a device for this test.") 103 private boolean mNullDeviceRequested = false; 104 105 @Option( 106 name = "gce-device", 107 description = "start a placeholder for a gce device that will be connected later.") 108 private boolean mGceDeviceRequested = false; 109 110 @Option(name = "device-type", description = "The type of the device requested to be allocated.") 111 private DeviceRequestedType mRequestedType = null; 112 113 @Option( 114 name = "base-device-type-request", 115 description = 116 "Explicitly request a device type which will use device-type for connection.") 117 private BaseDeviceType mBaseDeviceType = null; 118 // ============================ END DEVICE TYPE Related Options ============================ 119 120 @Option( 121 name = "min-battery", 122 description = 123 "only run this test on a device whose battery level is at least the given" 124 + " amount. Scale: 0-100", 125 updateRule = OptionUpdateRule.GREATEST) 126 private Integer mMinBattery = null; 127 128 @Option( 129 name = "max-battery", 130 description = 131 "only run this test on a device whose battery level is strictly less than the " 132 + "given amount. Scale: 0-100", 133 updateRule = OptionUpdateRule.LEAST) 134 private Integer mMaxBattery = null; 135 136 @Option( 137 name = "max-battery-temperature", 138 description = 139 "only run this test on a device whose battery temperature is strictly " 140 + "less than the given amount. Scale: Degrees celsius" 141 ) 142 private Integer mMaxBatteryTemperature = null; 143 144 @Option( 145 name = "require-battery-check", 146 description = 147 "_If_ --min-battery and/or " 148 + "--max-battery is specified, enforce the check. If " 149 + "require-battery-check=false, then no battery check will occur." 150 ) 151 private boolean mRequireBatteryCheck = true; 152 153 @Option( 154 name = "require-battery-temp-check", 155 description = 156 "_If_ --max-battery-temperature is specified, enforce the battery checking. If " 157 + "require-battery-temp-check=false, then no temperature check will occur." 158 ) 159 private boolean mRequireBatteryTemperatureCheck = true; 160 161 @Option(name = "min-sdk-level", description = "Only run this test on devices that support " + 162 "this Android SDK/API level") 163 private Integer mMinSdk = null; 164 165 @Option( 166 name = "max-sdk-level", 167 description = 168 "Only run this test on devices that are running " 169 + "this or lower Android SDK/API level") 170 private Integer mMaxSdk = null; 171 172 // If we have tried to fetch the environment variable ANDROID_SERIAL before. 173 private boolean mFetchedEnvVariable = false; 174 // Store the reason for which the device was not matched. 175 private Map<String, String> mNoMatchReason = new LinkedHashMap<>(); 176 // If we fail all allocation due to serial report a special message 177 private boolean mSerialMatch = false; 178 179 private static final String VARIANT_SEPARATOR = ":"; 180 181 /** 182 * Add a serial number to the device selection options. 183 * 184 * @param serialNumber 185 */ addSerial(String serialNumber)186 public void addSerial(String serialNumber) { 187 mSerials.add(serialNumber); 188 } 189 190 /** 191 * {@inheritDoc} 192 */ 193 @Override setSerial(String... serialNumber)194 public void setSerial(String... serialNumber) { 195 mSerials.clear(); 196 mSerials.addAll(Arrays.asList(serialNumber)); 197 } 198 199 /** {@inheritDoc} */ 200 @Override getSerials()201 public List<String> getSerials() { 202 return new ArrayList<>(mSerials); 203 } 204 205 /** {@inheritDoc} */ 206 @Override getSerials(IDevice device)207 public Collection<String> getSerials(IDevice device) { 208 // If no serial was explicitly set, use the environment variable ANDROID_SERIAL. 209 if (mSerials.isEmpty() && !mFetchedEnvVariable) { 210 String env_serial = fetchEnvironmentVariable("ANDROID_SERIAL"); 211 if (env_serial != null 212 && (!(device instanceof StubDevice) || (device instanceof FastbootDevice))) { 213 mSerials.add(env_serial); 214 } 215 mFetchedEnvVariable = true; 216 } 217 return copyCollection(mSerials); 218 } 219 220 /** 221 * Add a serial number to exclusion list. 222 * 223 * @param serialNumber 224 */ addExcludeSerial(String serialNumber)225 public void addExcludeSerial(String serialNumber) { 226 mExcludeSerials.add(serialNumber); 227 } 228 229 /** 230 * Add a product type to the device selection options. 231 * 232 * @param productType 233 */ addProductType(String productType)234 public void addProductType(String productType) { 235 mProductTypes.add(productType); 236 } 237 238 /** 239 * Add a property criteria to the device selection options 240 */ addProperty(String propertyKey, String propValue)241 public void addProperty(String propertyKey, String propValue) { 242 mPropertyMap.put(propertyKey, propValue); 243 } 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override getExcludeSerials()249 public Collection<String> getExcludeSerials() { 250 return copyCollection(mExcludeSerials); 251 } 252 253 /** 254 * {@inheritDoc} 255 */ 256 @Override getProductTypes()257 public Collection<String> getProductTypes() { 258 return copyCollection(mProductTypes); 259 } 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override deviceRequested()265 public boolean deviceRequested() { 266 return mDeviceRequested; 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override emulatorRequested()273 public boolean emulatorRequested() { 274 if (mRequestedType != null) { 275 return mRequestedType.equals(DeviceRequestedType.LOCAL_EMULATOR); 276 } 277 return mEmulatorRequested; 278 } 279 280 /** 281 * {@inheritDoc} 282 */ 283 @Override stubEmulatorRequested()284 public boolean stubEmulatorRequested() { 285 if (mRequestedType != null) { 286 return mRequestedType.equals(DeviceRequestedType.LOCAL_EMULATOR); 287 } 288 return mStubEmulatorRequested; 289 } 290 291 /** 292 * {@inheritDoc} 293 */ 294 @Override nullDeviceRequested()295 public boolean nullDeviceRequested() { 296 if (mRequestedType != null) { 297 return mRequestedType.equals(DeviceRequestedType.NULL_DEVICE); 298 } 299 return mNullDeviceRequested; 300 } 301 302 /** {@inheritDoc} */ 303 @Override gceDeviceRequested()304 public boolean gceDeviceRequested() { 305 if (mRequestedType != null) { 306 return mRequestedType.equals(DeviceRequestedType.GCE_DEVICE); 307 } 308 return mGceDeviceRequested; 309 } 310 setGceDeviceRequested(boolean gceDeviceRequested)311 public void setGceDeviceRequested(boolean gceDeviceRequested) { 312 mGceDeviceRequested = gceDeviceRequested; 313 } 314 remoteDeviceRequested()315 public boolean remoteDeviceRequested() { 316 return DeviceRequestedType.REMOTE_DEVICE.equals(mRequestedType); 317 } 318 localVirtualDeviceRequested()319 public boolean localVirtualDeviceRequested() { 320 return DeviceRequestedType.LOCAL_VIRTUAL_DEVICE.equals(mRequestedType); 321 } 322 323 /** 324 * Sets the emulator requested flag 325 */ setEmulatorRequested(boolean emulatorRequested)326 public void setEmulatorRequested(boolean emulatorRequested) { 327 mEmulatorRequested = emulatorRequested; 328 } 329 330 /** 331 * Sets the stub emulator requested flag 332 */ setStubEmulatorRequested(boolean stubEmulatorRequested)333 public void setStubEmulatorRequested(boolean stubEmulatorRequested) { 334 mStubEmulatorRequested = stubEmulatorRequested; 335 } 336 337 /** 338 * Sets the emulator requested flag 339 */ setDeviceRequested(boolean deviceRequested)340 public void setDeviceRequested(boolean deviceRequested) { 341 mDeviceRequested = deviceRequested; 342 } 343 344 /** 345 * Sets the null device requested flag 346 */ setNullDeviceRequested(boolean nullDeviceRequested)347 public void setNullDeviceRequested(boolean nullDeviceRequested) { 348 mNullDeviceRequested = nullDeviceRequested; 349 } 350 setDeviceTypeRequested(DeviceRequestedType requestedType)351 public void setDeviceTypeRequested(DeviceRequestedType requestedType) { 352 mRequestedType = requestedType; 353 } 354 getDeviceTypeRequested()355 public DeviceRequestedType getDeviceTypeRequested() { 356 return mRequestedType; 357 } 358 359 @Override getBaseDeviceTypeRequested()360 public BaseDeviceType getBaseDeviceTypeRequested() { 361 return mBaseDeviceType; 362 } 363 364 @Override setBaseDeviceTypeRequested(BaseDeviceType type)365 public void setBaseDeviceTypeRequested(BaseDeviceType type) { 366 mBaseDeviceType = type; 367 } 368 369 /** 370 * Sets the minimum battery level 371 */ setMinBatteryLevel(Integer minBattery)372 public void setMinBatteryLevel(Integer minBattery) { 373 mMinBattery = minBattery; 374 } 375 376 /** 377 * Gets the requested minimum battery level 378 */ getMinBatteryLevel()379 public Integer getMinBatteryLevel() { 380 return mMinBattery; 381 } 382 383 /** 384 * Sets the maximum battery level 385 */ setMaxBatteryLevel(Integer maxBattery)386 public void setMaxBatteryLevel(Integer maxBattery) { 387 mMaxBattery = maxBattery; 388 } 389 390 /** 391 * Gets the requested maximum battery level 392 */ getMaxBatteryLevel()393 public Integer getMaxBatteryLevel() { 394 return mMaxBattery; 395 } 396 397 /** Sets the maximum battery level */ setMaxBatteryTemperature(Integer maxBatteryTemperature)398 public void setMaxBatteryTemperature(Integer maxBatteryTemperature) { 399 mMaxBatteryTemperature = maxBatteryTemperature; 400 } 401 402 /** Gets the requested maximum battery level */ getMaxBatteryTemperature()403 public Integer getMaxBatteryTemperature() { 404 return mMaxBatteryTemperature; 405 } 406 407 /** Sets whether battery check is required for devices with unknown battery level */ 408 @Override setRequireBatteryCheck(boolean requireCheck)409 public void setRequireBatteryCheck(boolean requireCheck) { 410 mRequireBatteryCheck = requireCheck; 411 } 412 413 /** 414 * Gets whether battery check is required for devices with unknown battery level 415 */ getRequireBatteryCheck()416 public boolean getRequireBatteryCheck() { 417 return mRequireBatteryCheck; 418 } 419 420 /** Sets whether battery temp check is required for devices with unknown battery temperature */ setRequireBatteryTemperatureCheck(boolean requireCheckTemprature)421 public void setRequireBatteryTemperatureCheck(boolean requireCheckTemprature) { 422 mRequireBatteryTemperatureCheck = requireCheckTemprature; 423 } 424 425 /** Gets whether battery temp check is required for devices with unknown battery temperature */ getRequireBatteryTemperatureCheck()426 public boolean getRequireBatteryTemperatureCheck() { 427 return mRequireBatteryTemperatureCheck; 428 } 429 430 /** 431 * {@inheritDoc} 432 */ 433 @Override getProperties()434 public Map<String, String> getProperties() { 435 return mPropertyMap; 436 } 437 copyCollection(Collection<String> original)438 private Collection<String> copyCollection(Collection<String> original) { 439 Collection<String> listCopy = new ArrayList<String>(original.size()); 440 listCopy.addAll(original); 441 return listCopy; 442 } 443 444 /** 445 * Helper function used to fetch environment variable. It is essentially a wrapper around {@link 446 * System#getenv(String)} This is done for unit testing purposes. 447 * 448 * @param name the environment variable to fetch. 449 * @return a {@link String} value of the environment variable or null if not available. 450 */ 451 @VisibleForTesting fetchEnvironmentVariable(String name)452 public String fetchEnvironmentVariable(String name) { 453 return System.getenv(name); 454 } 455 456 /** 457 * @return <code>true</code> if the given {@link IDevice} is a match for the provided options. 458 * <code>false</code> otherwise 459 */ 460 @Override matches(IDevice device)461 public boolean matches(IDevice device) { 462 String deviceSerial = device.getSerialNumber(); 463 Collection<String> serials = getSerials(device); 464 Collection<String> excludeSerials = getExcludeSerials(); 465 Map<String, Collection<String>> productVariants = splitOnVariant(getProductTypes()); 466 Collection<String> productTypes = productVariants.keySet(); 467 Map<String, String> properties = getProperties(); 468 469 if (!serials.isEmpty() && 470 !serials.contains(device.getSerialNumber())) { 471 // Don't add a reason here, if the serial doesn't even match it's just verbose 472 return false; 473 } 474 mSerialMatch = true; 475 if (excludeSerials.contains(device.getSerialNumber())) { 476 addNoMatchReason( 477 deviceSerial, 478 String.format( 479 "device serial was part of excluded serials(%s)", excludeSerials)); 480 return false; 481 } 482 if (!productTypes.isEmpty()) { 483 String productType = getDeviceProductType(device); 484 if (productTypes.contains(productType)) { 485 // check variant 486 String productVariant = getDeviceProductVariant(device); 487 Collection<String> variants = productVariants.get(productType); 488 if (variants != null && !variants.contains(productVariant)) { 489 addNoMatchReason( 490 deviceSerial, 491 String.format( 492 "device variant (%s) does not match requested variants(%s)", 493 productVariant, variants)); 494 return false; 495 } 496 } else { 497 // no product type matches; bye-bye 498 addNoMatchReason( 499 deviceSerial, 500 String.format( 501 "device product type (%s) does not match requested product" 502 + " types(%s)", 503 productType, productTypes)); 504 return false; 505 } 506 } 507 for (Map.Entry<String, String> propEntry : properties.entrySet()) { 508 String deviceProperty = device.getProperty(propEntry.getKey()); 509 if (!propEntry.getValue().equals(deviceProperty)) { 510 addNoMatchReason( 511 deviceSerial, 512 String.format( 513 "device property (%s) value(%s) does not match requested value(%s)", 514 propEntry.getKey(), deviceProperty, propEntry.getValue())); 515 return false; 516 } 517 } 518 // Check if the device match the requested type 519 if (!checkDeviceTypeRequested(device)) { 520 return false; 521 } 522 523 if ((mMinSdk != null) || (mMaxSdk != null)) { 524 int deviceSdkLevel = getDeviceSdkLevel(device); 525 if (deviceSdkLevel < 0) { 526 addNoMatchReason( 527 deviceSerial, 528 String.format("device returned unexpected sdk level (%s)", deviceSdkLevel)); 529 return false; 530 } 531 if (mMinSdk != null && deviceSdkLevel < mMinSdk) { 532 addNoMatchReason( 533 deviceSerial, 534 String.format( 535 "device sdk (%s) is below the requested min sdk (%s)", 536 deviceSdkLevel, mMinSdk)); 537 return false; 538 } 539 if (mMaxSdk != null && mMaxSdk < deviceSdkLevel) { 540 addNoMatchReason( 541 deviceSerial, 542 String.format( 543 "device sdk (%s) is above the requested max sdk (%s)", 544 deviceSdkLevel, mMaxSdk)); 545 return false; 546 } 547 } 548 // If battery check is required and we have a min/max battery requested 549 if (mRequireBatteryCheck) { 550 if ((mMinBattery != null || mMaxBattery != null)) { 551 // Only check battery on physical device. (FastbootDevice placeholder is always for 552 // a physical device 553 if (device instanceof StubDevice || device instanceof FastbootDevice) { 554 // Reading battery of fastboot and StubDevice device does not work and could 555 // lead to weird log. 556 addNoMatchReason( 557 deviceSerial, 558 String.format( 559 "device type is (%s) which cannot have a battery required.", 560 device.getClass())); 561 return false; 562 } 563 Integer deviceBattery = getBatteryLevel(device); 564 if (deviceBattery == null) { 565 // Couldn't determine battery level when that check is required; reject device 566 addNoMatchReason(deviceSerial, "device failed to return a battery reading."); 567 return false; 568 } 569 if (isLessAndNotNull(deviceBattery, mMinBattery)) { 570 // deviceBattery < mMinBattery 571 addNoMatchReason( 572 deviceSerial, 573 String.format( 574 "device battery (%s) is below the requested min battery (%s)", 575 deviceBattery, mMinBattery)); 576 return false; 577 } 578 if (isLessEqAndNotNull(mMaxBattery, deviceBattery)) { 579 // mMaxBattery <= deviceBattery 580 addNoMatchReason( 581 deviceSerial, 582 String.format( 583 "device battery (%s) is above the requested max battery (%s)", 584 deviceBattery, mMaxBattery)); 585 return false; 586 } 587 } 588 } 589 // If temperature check is required and we have a max temperature requested. 590 if (mRequireBatteryTemperatureCheck) { 591 if (mMaxBatteryTemperature != null 592 && (!(device instanceof StubDevice) || (device instanceof FastbootDevice))) { 593 // Only check battery temp on physical device. (FastbootDevice placeholder is 594 // always for a physical device 595 596 if (device instanceof FastbootDevice) { 597 // Cannot get battery temperature 598 return false; 599 } 600 601 // Extract the temperature from the file 602 BatteryTemperature temp = new BatteryTemperature(); 603 Integer deviceBatteryTemp = temp.getBatteryTemperature(device); 604 605 if (deviceBatteryTemp <= 0) { 606 // Couldn't determine battery temp when that check is required; reject device 607 return false; 608 } 609 610 if (isLessEqAndNotNull(mMaxBatteryTemperature, deviceBatteryTemp)) { 611 // mMaxBatteryTemperature <= deviceBatteryTemp 612 return false; 613 } 614 } 615 } 616 617 return true; 618 } 619 620 /** Determine whether a device match the requested type or not. */ checkDeviceTypeRequested(IDevice device)621 private boolean checkDeviceTypeRequested(IDevice device) { 622 if ((emulatorRequested() || stubEmulatorRequested()) && !device.isEmulator()) { 623 return false; 624 } 625 String deviceSerial = device.getSerialNumber(); 626 // If physical device is requested but device is emulator or remote ip device, skip 627 if (deviceRequested() 628 && (device.isEmulator() 629 || RemoteAndroidDevice.checkSerialFormatValid(device.getSerialNumber()))) { 630 addNoMatchReason(deviceSerial, "device is not a physical device"); 631 return false; 632 } 633 634 if (mRequestedType != null) { 635 Class<?> classNeeded = mRequestedType.getRequiredClass(); 636 // Don't match IDevice for real device 637 if (!DeviceRequestedType.EXISTING_DEVICE.equals(mRequestedType)) { 638 if (!device.getClass().equals(classNeeded)) { 639 addNoMatchReason( 640 deviceSerial, 641 String.format( 642 "device is type (%s) while requested type was (%s)", 643 device.getClass(), classNeeded)); 644 return false; 645 } 646 } 647 } else { 648 if (device.isEmulator() && (device instanceof StubDevice) && !stubEmulatorRequested()) { 649 // only allocate the stub emulator if requested 650 addNoMatchReason(deviceSerial, "device is emulator while requested type was not"); 651 return false; 652 } 653 if (nullDeviceRequested() != (device instanceof NullDevice)) { 654 addNoMatchReason( 655 deviceSerial, "device is null-device while requested type was not"); 656 return false; 657 } 658 if (gceDeviceRequested() != RemoteAvdIDevice.class.equals(device.getClass())) { 659 // We only match an exact RemoteAvdIDevice here, no child class. 660 addNoMatchReason(deviceSerial, "device is gce-device while requested type was not"); 661 return false; 662 } 663 if (remoteDeviceRequested() != VmRemoteDevice.class.equals(device.getClass())) { 664 addNoMatchReason( 665 deviceSerial, "device is remote-device while requested type was not"); 666 return false; 667 } 668 if (localVirtualDeviceRequested() 669 != StubLocalAndroidVirtualDevice.class.equals(device.getClass())) { 670 addNoMatchReason( 671 deviceSerial, "device is local-virtual while requested type was not"); 672 return false; 673 } 674 } 675 return true; 676 } 677 678 /** Determine if x is less-than y, given that both are non-Null */ isLessAndNotNull(Integer x, Integer y)679 private static boolean isLessAndNotNull(Integer x, Integer y) { 680 if ((x == null) || (y == null)) { 681 return false; 682 } 683 return x < y; 684 } 685 686 /** Determine if x is less-than y, given that both are non-Null */ isLessEqAndNotNull(Integer x, Integer y)687 private static boolean isLessEqAndNotNull(Integer x, Integer y) { 688 if ((x == null) || (y == null)) { 689 return false; 690 } 691 return x <= y; 692 } 693 splitOnVariant(Collection<String> products)694 private Map<String, Collection<String>> splitOnVariant(Collection<String> products) { 695 // FIXME: we should validate all provided device selection options once, on the first 696 // FIXME: call to #matches 697 Map<String, Collection<String>> splitProducts = 698 new HashMap<String, Collection<String>>(products.size()); 699 // FIXME: cache this 700 for (String prod : products) { 701 String[] parts = prod.split(VARIANT_SEPARATOR); 702 if (parts.length == 1) { 703 splitProducts.put(parts[0], null); 704 } else if (parts.length == 2) { 705 // A variant was specified as product:variant 706 Collection<String> variants = splitProducts.get(parts[0]); 707 if (variants == null) { 708 variants = new HashSet<String>(); 709 splitProducts.put(parts[0], variants); 710 } 711 variants.add(parts[1]); 712 } else { 713 throw new IllegalArgumentException(String.format("The product type filter \"%s\" " + 714 "is invalid. It must contain 0 or 1 '%s' characters, not %d.", 715 prod, VARIANT_SEPARATOR, parts.length)); 716 } 717 } 718 719 return splitProducts; 720 } 721 722 @Override getDeviceProductType(IDevice device)723 public String getDeviceProductType(IDevice device) { 724 String prop = getProperty(device, DeviceProperties.BOARD); 725 // fallback to ro.hardware for legacy devices 726 if (Strings.isNullOrEmpty(prop)) { 727 prop = getProperty(device, DeviceProperties.HARDWARE); 728 } 729 if (prop != null) { 730 prop = prop.toLowerCase(); 731 } 732 return prop; 733 } 734 getProperty(IDevice device, String propName)735 private String getProperty(IDevice device, String propName) { 736 return device.getProperty(propName); 737 } 738 739 @Override getDeviceProductVariant(IDevice device)740 public String getDeviceProductVariant(IDevice device) { 741 String prop = getProperty(device, DeviceProperties.VARIANT); 742 if (prop == null) { 743 prop = getProperty(device, DeviceProperties.VARIANT_LEGACY_O_MR1); 744 } 745 if (prop == null) { 746 prop = getProperty(device, DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O); 747 } 748 if (prop != null) { 749 prop = prop.toLowerCase(); 750 } 751 return prop; 752 } 753 754 @Override getBatteryLevel(IDevice device)755 public Integer getBatteryLevel(IDevice device) { 756 try { 757 // use default 5 minutes freshness 758 Future<Integer> batteryFuture = device.getBattery(); 759 // get cached value or wait up to 500ms for battery level query 760 return batteryFuture.get(500, TimeUnit.MILLISECONDS); 761 } catch (InterruptedException | ExecutionException | 762 java.util.concurrent.TimeoutException e) { 763 CLog.w("Failed to query battery level for %s: %s", device.getSerialNumber(), 764 e.toString()); 765 } 766 return null; 767 } 768 769 /** 770 * Get the device's supported API level or -1 if it cannot be retrieved 771 * @param device 772 * @return the device's supported API level. 773 */ getDeviceSdkLevel(IDevice device)774 private int getDeviceSdkLevel(IDevice device) { 775 int apiLevel = -1; 776 String prop = getProperty(device, DeviceProperties.SDK_VERSION); 777 try { 778 apiLevel = Integer.parseInt(prop); 779 } catch (NumberFormatException nfe) { 780 CLog.w("Failed to parse sdk level %s for device %s", prop, device.getSerialNumber()); 781 } 782 return apiLevel; 783 } 784 addNoMatchReason(String device, String reason)785 private void addNoMatchReason(String device, String reason) { 786 mNoMatchReason.put(device, reason); 787 } 788 789 @Override getNoMatchReason()790 public Map<String, String> getNoMatchReason() { 791 if (!mSerialMatch) { 792 mNoMatchReason.put( 793 "no_match", 794 String.format("Need serial (%s) but couldn't match it.", getSerials())); 795 } 796 mSerialMatch = false; 797 return mNoMatchReason; 798 } 799 800 /** 801 * Helper factory method to create a {@link IDeviceSelection} that will only match device 802 * with given serial 803 */ createForSerial(String serial)804 public static IDeviceSelection createForSerial(String serial) { 805 DeviceSelectionOptions o = new DeviceSelectionOptions(); 806 o.setSerial(serial); 807 return o; 808 } 809 } 810