Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(184)

Side by Side Diff: build/android/pylib/device/device_utils.py

Issue 1166113002: Add InstallSplitApk function to device utils. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed comments. Moved split_select to pylib/sdk. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Provides a variety of device interactions based on adb. 5 """Provides a variety of device interactions based on adb.
6 6
7 Eventually, this will be based on adb_wrapper. 7 Eventually, this will be based on adb_wrapper.
8 """ 8 """
9 # pylint: disable=unused-argument 9 # pylint: disable=unused-argument
10 10
(...skipping 15 matching lines...) Expand all
26 from pylib import cmd_helper 26 from pylib import cmd_helper
27 from pylib import constants 27 from pylib import constants
28 from pylib import device_signal 28 from pylib import device_signal
29 from pylib.device import adb_wrapper 29 from pylib.device import adb_wrapper
30 from pylib.device import decorators 30 from pylib.device import decorators
31 from pylib.device import device_blacklist 31 from pylib.device import device_blacklist
32 from pylib.device import device_errors 32 from pylib.device import device_errors
33 from pylib.device import intent 33 from pylib.device import intent
34 from pylib.device import logcat_monitor 34 from pylib.device import logcat_monitor
35 from pylib.device.commands import install_commands 35 from pylib.device.commands import install_commands
36 from pylib.sdk import split_select
36 from pylib.utils import apk_helper 37 from pylib.utils import apk_helper
37 from pylib.utils import base_error 38 from pylib.utils import base_error
38 from pylib.utils import device_temp_file 39 from pylib.utils import device_temp_file
39 from pylib.utils import host_utils 40 from pylib.utils import host_utils
40 from pylib.utils import md5sum 41 from pylib.utils import md5sum
41 from pylib.utils import parallelizer 42 from pylib.utils import parallelizer
42 from pylib.utils import timeout_retry 43 from pylib.utils import timeout_retry
43 from pylib.utils import zip_utils 44 from pylib.utils import zip_utils
44 45
45 _DEFAULT_TIMEOUT = 30 46 _DEFAULT_TIMEOUT = 30
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', 333 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
333 single_line=True, 334 single_line=True,
334 check_return=True) 335 check_return=True)
335 if not value: 336 if not value:
336 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', 337 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
337 str(self)) 338 str(self))
338 self._cache['external_storage'] = value 339 self._cache['external_storage'] = value
339 return value 340 return value
340 341
341 @decorators.WithTimeoutAndRetriesFromInstance() 342 @decorators.WithTimeoutAndRetriesFromInstance()
342 def GetApplicationPath(self, package, timeout=None, retries=None): 343 def GetApplicationPaths(self, package, timeout=None, retries=None):
343 """Get the path of the installed apk on the device for the given package. 344 """Get the paths of the installed apks on the device for the given package.
344 345
345 Args: 346 Args:
346 package: Name of the package. 347 package: Name of the package.
347 348
348 Returns: 349 Returns:
349 Path to the apk on the device if it exists, None otherwise. 350 List of paths to the apks on the device if they exists, None otherwise.
jbudorick 2015/06/19 17:32:13 nit: "None otherwise" is no longer accurate.
350 """ 351 """
351 # 'pm path' is liable to incorrectly exit with a nonzero number starting 352 # 'pm path' is liable to incorrectly exit with a nonzero number starting
352 # in Lollipop. 353 # in Lollipop.
353 # TODO(jbudorick): Check if this is fixed as new Android versions are 354 # TODO(jbudorick): Check if this is fixed as new Android versions are
354 # released to put an upper bound on this. 355 # released to put an upper bound on this.
355 should_check_return = (self.build_version_sdk < 356 should_check_return = (self.build_version_sdk <
356 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) 357 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
357 output = self.RunShellCommand(['pm', 'path', package], single_line=True, 358 output = self.RunShellCommand(
358 check_return=should_check_return) 359 ['pm', 'path', package], check_return=should_check_return)
359 if not output: 360 apks = []
360 return None 361 for line in output:
361 if not output.startswith('package:'): 362 if not line.startswith('package:'):
362 raise device_errors.CommandFailedError('pm path returned: %r' % output, 363 raise device_errors.CommandFailedError(
363 str(self)) 364 'pm path returned: %r' % '\n'.join(output), str(self))
364 return output[len('package:'):] 365 apks.append(line[len('package:'):])
366 return apks
365 367
366 @decorators.WithTimeoutAndRetriesFromInstance() 368 @decorators.WithTimeoutAndRetriesFromInstance()
367 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 369 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
368 """Get the data directory on the device for the given package. 370 """Get the data directory on the device for the given package.
369 371
370 Args: 372 Args:
371 package: Name of the package. 373 package: Name of the package.
372 374
373 Returns: 375 Returns:
374 The package's data directory, or None if the package doesn't exist on the 376 The package's data directory, or None if the package doesn't exist on the
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 def sd_card_ready(): 408 def sd_card_ready():
407 try: 409 try:
408 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 410 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
409 check_return=True) 411 check_return=True)
410 return True 412 return True
411 except device_errors.AdbCommandFailedError: 413 except device_errors.AdbCommandFailedError:
412 return False 414 return False
413 415
414 def pm_ready(): 416 def pm_ready():
415 try: 417 try:
416 return self.GetApplicationPath('android') 418 return self.GetApplicationPaths('android')
417 except device_errors.CommandFailedError: 419 except device_errors.CommandFailedError:
418 return False 420 return False
419 421
420 def boot_completed(): 422 def boot_completed():
421 return self.GetProp('sys.boot_completed') == '1' 423 return self.GetProp('sys.boot_completed') == '1'
422 424
423 def wifi_enabled(): 425 def wifi_enabled():
424 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 426 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
425 check_return=False) 427 check_return=False)
426 428
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 reinstall: A boolean indicating if we should keep any existing app data. 478 reinstall: A boolean indicating if we should keep any existing app data.
477 timeout: timeout in seconds 479 timeout: timeout in seconds
478 retries: number of retries 480 retries: number of retries
479 481
480 Raises: 482 Raises:
481 CommandFailedError if the installation fails. 483 CommandFailedError if the installation fails.
482 CommandTimeoutError if the installation times out. 484 CommandTimeoutError if the installation times out.
483 DeviceUnreachableError on missing device. 485 DeviceUnreachableError on missing device.
484 """ 486 """
485 package_name = apk_helper.GetPackageName(apk_path) 487 package_name = apk_helper.GetPackageName(apk_path)
486 device_path = self.GetApplicationPath(package_name) 488 device_paths = self.GetApplicationPaths(package_name)
487 if device_path is not None: 489 if device_paths:
490 if len(device_paths) > 1:
491 logging.warning(
492 'Installing single APK (%s) when split APKs (%s) are currently '
493 'installed.', apk_path, ' '.join(device_paths))
488 (files_to_push, _) = self._GetChangedAndStaleFiles( 494 (files_to_push, _) = self._GetChangedAndStaleFiles(
489 apk_path, device_path) 495 apk_path, device_paths[0])
490 should_install = bool(files_to_push) 496 should_install = bool(files_to_push)
491 if should_install and not reinstall: 497 if should_install and not reinstall:
492 self.adb.Uninstall(package_name) 498 self.adb.Uninstall(package_name)
493 else: 499 else:
494 should_install = True 500 should_install = True
495 if should_install: 501 if should_install:
496 self.adb.Install(apk_path, reinstall=reinstall) 502 self.adb.Install(apk_path, reinstall=reinstall)
497 503
504 @decorators.WithTimeoutAndRetriesDefaults(
505 INSTALL_DEFAULT_TIMEOUT,
506 INSTALL_DEFAULT_RETRIES)
507 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
508 timeout=None, retries=None):
509 """Install a split APK.
510
511 Noop if all of the APK splits are already installed.
512
513 Args:
514 base_apk: A string of the path to the base APK.
515 split_apks: A list of strings of paths of all of the APK splits.
516 reinstall: A boolean indicating if we should keep any existing app data.
517 timeout: timeout in seconds
518 retries: number of retries
519
520 Raises:
521 CommandFailedError if the installation fails.
522 CommandTimeoutError if the installation times out.
523 DeviceUnreachableError on missing device.
524 DeviceVersionError if device SDK is less than Android L.
525 """
526 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
527
528 all_apks = [base_apk] + split_select.SelectSplits(
529 self, base_apk, split_apks)
530 package_name = apk_helper.GetPackageName(base_apk)
531 device_apk_paths = self.GetApplicationPaths(package_name)
532
533 if device_apk_paths:
534 partial_install_package = package_name
535 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self)
536 host_checksums = md5sum.CalculateHostMd5Sums(all_apks)
537 apks_to_install = [k for (k, v) in host_checksums.iteritems()
538 if v not in device_checksums.values()]
539 if apks_to_install and not reinstall:
540 self.adb.Uninstall(package_name)
541 partial_install_package = None
542 apks_to_install = all_apks
543 else:
544 partial_install_package = None
545 apks_to_install = all_apks
546 if apks_to_install:
547 self.adb.InstallMultiple(
548 apks_to_install, partial=partial_install_package, reinstall=reinstall)
549
550 def _CheckSdkLevel(self, required_sdk_level):
551 """Raises an exception if the device does not have the required SDK level.
552 """
553 if self.build_version_sdk < required_sdk_level:
554 raise device_errors.DeviceVersionError(
555 ('Requires SDK level %s, device is SDK level %s' %
556 (required_sdk_level, self.build_version_sdk)),
557 device_serial=self.adb.GetDeviceSerial())
558
559
498 @decorators.WithTimeoutAndRetriesFromInstance() 560 @decorators.WithTimeoutAndRetriesFromInstance()
499 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, 561 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
500 as_root=False, single_line=False, large_output=False, 562 as_root=False, single_line=False, large_output=False,
501 timeout=None, retries=None): 563 timeout=None, retries=None):
502 """Run an ADB shell command. 564 """Run an ADB shell command.
503 565
504 The command to run |cmd| should be a sequence of program arguments or else 566 The command to run |cmd| should be a sequence of program arguments or else
505 a single string. 567 a single string.
506 568
507 When |cmd| is a sequence, it is assumed to contain the name of the command 569 When |cmd| is a sequence, it is assumed to contain the name of the command
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
799 861
800 Raises: 862 Raises:
801 CommandTimeoutError on timeout. 863 CommandTimeoutError on timeout.
802 DeviceUnreachableError on missing device. 864 DeviceUnreachableError on missing device.
803 """ 865 """
804 # Check that the package exists before clearing it for android builds below 866 # Check that the package exists before clearing it for android builds below
805 # JB MR2. Necessary because calling pm clear on a package that doesn't exist 867 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
806 # may never return. 868 # may never return.
807 if ((self.build_version_sdk >= 869 if ((self.build_version_sdk >=
808 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) 870 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
809 or self.GetApplicationPath(package)): 871 or self.GetApplicationPaths(package)):
810 self.RunShellCommand(['pm', 'clear', package], check_return=True) 872 self.RunShellCommand(['pm', 'clear', package], check_return=True)
811 873
812 @decorators.WithTimeoutAndRetriesFromInstance() 874 @decorators.WithTimeoutAndRetriesFromInstance()
813 def SendKeyEvent(self, keycode, timeout=None, retries=None): 875 def SendKeyEvent(self, keycode, timeout=None, retries=None):
814 """Sends a keycode to the device. 876 """Sends a keycode to the device.
815 877
816 See the pylib.constants.keyevent module for suitable keycode values. 878 See the pylib.constants.keyevent module for suitable keycode values.
817 879
818 Args: 880 Args:
819 keycode: A integer keycode to send to the device. 881 keycode: A integer keycode to send to the device.
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after
1286 1348
1287 # Next, check the current runtime value is what we need, and 1349 # Next, check the current runtime value is what we need, and
1288 # if not, set it and report that a reboot is required. 1350 # if not, set it and report that a reboot is required.
1289 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) 1351 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1290 if new_value != value: 1352 if new_value != value:
1291 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) 1353 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1292 return True 1354 return True
1293 else: 1355 else:
1294 return False 1356 return False
1295 1357
1358 @property
1359 def langauge(self):
jbudorick 2015/06/19 17:32:13 nit: langauge -> language
1360 """Returns the language setting on the device."""
1361 return self.GetProp('persist.sys.language', cache=False)
1362
1363 @property
1364 def country(self):
1365 """Returns the country setting on the device."""
1366 return self.GetProp('persist.sys.country', cache=False)
1367
1368 @property
1369 def screen_density(self):
1370 """Returns the screen density of the device."""
1371 DPI_TO_DENSITY = {
1372 120: 'ldpi',
1373 160: 'mdpi',
1374 240: 'hdpi',
1375 320: 'xhdpi',
1376 480: 'xxhdpi',
1377 }
1378 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True))
1379 return DPI_TO_DENSITY.get(dpi, 'tvdpi')
1296 1380
1297 @property 1381 @property
1298 def build_description(self): 1382 def build_description(self):
1299 """Returns the build description of the system. 1383 """Returns the build description of the system.
1300 1384
1301 For example: 1385 For example:
1302 nakasi-user 4.4.4 KTU84P 1227136 release-keys 1386 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1303 """ 1387 """
1304 return self.GetProp('ro.build.description', cache=True) 1388 return self.GetProp('ro.build.description', cache=True)
1305 1389
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
1626 @classmethod 1710 @classmethod
1627 def HealthyDevices(cls): 1711 def HealthyDevices(cls):
1628 blacklist = device_blacklist.ReadBlacklist() 1712 blacklist = device_blacklist.ReadBlacklist()
1629 def blacklisted(adb): 1713 def blacklisted(adb):
1630 if adb.GetDeviceSerial() in blacklist: 1714 if adb.GetDeviceSerial() in blacklist:
1631 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) 1715 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1632 return True 1716 return True
1633 return False 1717 return False
1634 1718
1635 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() 1719 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1636 if not blacklisted(adb)] 1720 if not blacklisted(adb)]
1637
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698