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

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: 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 14 matching lines...) Expand all
25 import pylib.android_commands 25 import pylib.android_commands
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 import split_select_wrapper
35 from pylib.device.commands import install_commands 36 from pylib.device.commands import install_commands
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
(...skipping 287 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.
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 if not output:
360 return None 361 return None
361 if not output.startswith('package:'): 362 if not all([s.startswith('package:') for s in output]):
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 return [s[len('package:'):] for s in output]
perezju 2015/06/18 09:30:17 This is walking over the output twice and building
mikecase (-- gone --) 2015/06/18 19:22:20 Now only iterate through list once. Removed specia
365 366
366 @decorators.WithTimeoutAndRetriesFromInstance() 367 @decorators.WithTimeoutAndRetriesFromInstance()
367 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 368 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
368 """Get the data directory on the device for the given package. 369 """Get the data directory on the device for the given package.
369 370
370 Args: 371 Args:
371 package: Name of the package. 372 package: Name of the package.
372 373
373 Returns: 374 Returns:
374 The package's data directory, or None if the package doesn't exist on the 375 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(): 407 def sd_card_ready():
407 try: 408 try:
408 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 409 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
409 check_return=True) 410 check_return=True)
410 return True 411 return True
411 except device_errors.AdbCommandFailedError: 412 except device_errors.AdbCommandFailedError:
412 return False 413 return False
413 414
414 def pm_ready(): 415 def pm_ready():
415 try: 416 try:
416 return self.GetApplicationPath('android') 417 return self.GetApplicationPaths('android')
417 except device_errors.CommandFailedError: 418 except device_errors.CommandFailedError:
418 return False 419 return False
419 420
420 def boot_completed(): 421 def boot_completed():
421 return self.GetProp('sys.boot_completed') == '1' 422 return self.GetProp('sys.boot_completed') == '1'
422 423
423 def wifi_enabled(): 424 def wifi_enabled():
424 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 425 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
425 check_return=False) 426 check_return=False)
426 427
(...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. 477 reinstall: A boolean indicating if we should keep any existing app data.
477 timeout: timeout in seconds 478 timeout: timeout in seconds
478 retries: number of retries 479 retries: number of retries
479 480
480 Raises: 481 Raises:
481 CommandFailedError if the installation fails. 482 CommandFailedError if the installation fails.
482 CommandTimeoutError if the installation times out. 483 CommandTimeoutError if the installation times out.
483 DeviceUnreachableError on missing device. 484 DeviceUnreachableError on missing device.
484 """ 485 """
485 package_name = apk_helper.GetPackageName(apk_path) 486 package_name = apk_helper.GetPackageName(apk_path)
486 device_path = self.GetApplicationPath(package_name) 487 device_paths = self.GetApplicationPaths(package_name)
487 if device_path is not None: 488 if device_paths:
488 (files_to_push, _) = self._GetChangedAndStaleFiles( 489 (files_to_push, _) = self._GetChangedAndStaleFiles(
perezju 2015/06/18 09:30:17 maybe add a warning if device_paths has more than
mikecase (-- gone --) 2015/06/18 19:22:20 Done
489 apk_path, device_path) 490 apk_path, device_paths[0])
490 should_install = bool(files_to_push) 491 should_install = bool(files_to_push)
491 if should_install and not reinstall: 492 if should_install and not reinstall:
492 self.adb.Uninstall(package_name) 493 self.adb.Uninstall(package_name)
493 else: 494 else:
494 should_install = True 495 should_install = True
495 if should_install: 496 if should_install:
496 self.adb.Install(apk_path, reinstall=reinstall) 497 self.adb.Install(apk_path, reinstall=reinstall)
497 498
499 @decorators.WithTimeoutAndRetriesDefaults(
500 INSTALL_DEFAULT_TIMEOUT,
501 INSTALL_DEFAULT_RETRIES)
502 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
perezju 2015/06/18 09:30:17 This is duplicating a lot of the code in Install.
mikecase (-- gone --) 2015/06/18 19:22:20 Added some more to the InstallSplitApk function so
503 timeout=None, retries=None):
504 """Install a split APK.
505
506 Noop if all of the APK splits are already installed.
507
508 Args:
509 base_apk: A string of the path to the base APK.
510 split_apks: A list of strings of paths of all of the APK splits.
511 reinstall: A boolean indicating if we should keep any existing app data.
512 timeout: timeout in seconds
513 retries: number of retries
514
515 Raises:
516 CommandFailedError if the installation fails.
517 CommandTimeoutError if the installation times out.
518 DeviceUnreachableError on missing device.
519 DeviceVersionError if device SDK is less than Android L.
520 """
521 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
522
523 required_splits = split_select_wrapper.SelectSplits(
524 self, base_apk, split_apks)
525 package_name = apk_helper.GetPackageName(base_apk)
526 device_apk_paths = self.GetApplicationPaths(package_name)
527
528 if device_apk_paths:
529 device_checksums = md5sum.CalculateDeviceMd5Sums(
530 device_apk_paths, self).values()
531 host_checksums = md5sum.CalculateHostMd5Sums(
532 [base_apk] + required_splits).values()
533 should_install = sorted(host_checksums) != sorted(device_checksums)
534 if should_install and not reinstall:
535 self.adb.Uninstall(package_name)
536 else:
537 should_install = True
538 if should_install:
539 self.adb.InstallMultiple(
agrieve 2015/06/18 14:01:08 It would be great to only install the splits that
mikecase (-- gone --) 2015/06/18 19:22:20 Done.
540 [base_apk] + required_splits, reinstall=reinstall)
541
542 def _CheckSdkLevel(self, required_sdk_level):
543 """Raises an exception if the device does not have the required SDK level.
544 """
545 if self.build_version_sdk < required_sdk_level:
546 raise device_errors.DeviceVersionError(
547 ('Requires SDK level %s, device is SDK level %s' %
548 (required_sdk_level, self.build_version_sdk)),
549 device_serial=self.adb.GetDeviceSerial())
550
551
498 @decorators.WithTimeoutAndRetriesFromInstance() 552 @decorators.WithTimeoutAndRetriesFromInstance()
499 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, 553 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
500 as_root=False, single_line=False, large_output=False, 554 as_root=False, single_line=False, large_output=False,
501 timeout=None, retries=None): 555 timeout=None, retries=None):
502 """Run an ADB shell command. 556 """Run an ADB shell command.
503 557
504 The command to run |cmd| should be a sequence of program arguments or else 558 The command to run |cmd| should be a sequence of program arguments or else
505 a single string. 559 a single string.
506 560
507 When |cmd| is a sequence, it is assumed to contain the name of the command 561 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 853
800 Raises: 854 Raises:
801 CommandTimeoutError on timeout. 855 CommandTimeoutError on timeout.
802 DeviceUnreachableError on missing device. 856 DeviceUnreachableError on missing device.
803 """ 857 """
804 # Check that the package exists before clearing it for android builds below 858 # 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 859 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
806 # may never return. 860 # may never return.
807 if ((self.build_version_sdk >= 861 if ((self.build_version_sdk >=
808 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) 862 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
809 or self.GetApplicationPath(package)): 863 or self.GetApplicationPaths(package)):
810 self.RunShellCommand(['pm', 'clear', package], check_return=True) 864 self.RunShellCommand(['pm', 'clear', package], check_return=True)
811 865
812 @decorators.WithTimeoutAndRetriesFromInstance() 866 @decorators.WithTimeoutAndRetriesFromInstance()
813 def SendKeyEvent(self, keycode, timeout=None, retries=None): 867 def SendKeyEvent(self, keycode, timeout=None, retries=None):
814 """Sends a keycode to the device. 868 """Sends a keycode to the device.
815 869
816 See the pylib.constants.keyevent module for suitable keycode values. 870 See the pylib.constants.keyevent module for suitable keycode values.
817 871
818 Args: 872 Args:
819 keycode: A integer keycode to send to the device. 873 keycode: A integer keycode to send to the device.
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after
1286 1340
1287 # Next, check the current runtime value is what we need, and 1341 # Next, check the current runtime value is what we need, and
1288 # if not, set it and report that a reboot is required. 1342 # if not, set it and report that a reboot is required.
1289 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) 1343 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1290 if new_value != value: 1344 if new_value != value:
1291 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) 1345 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1292 return True 1346 return True
1293 else: 1347 else:
1294 return False 1348 return False
1295 1349
1350 @property
1351 def langauge_setting(self):
1352 """Returns the language setting on the device."""
1353 return self.GetProp('persist.sys.language', cache=False)
1354
1355 @property
1356 def country_setting(self):
1357 """Returns the country setting on the device."""
1358 return self.GetProp('persist.sys.country', cache=False)
1359
1360 @property
1361 def screen_density(self):
1362 """Returns the screen density of the device."""
1363 DPI_TO_DENSITY = {
perezju 2015/06/18 09:30:16 not sure, but maybe this dict should be a private
mikecase (-- gone --) 2015/06/18 19:22:20 Leaving this here for now. Nothing else needs this
1364 120: 'ldpi',
1365 160: 'mdpi',
1366 240: 'hdpi',
1367 320: 'xhdpi',
1368 480: 'xxhdpi',
1369 }
1370 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True))
1371 return DPI_TO_DENSITY.get(dpi, 'tvdpi')
1296 1372
1297 @property 1373 @property
1298 def build_description(self): 1374 def build_description(self):
1299 """Returns the build description of the system. 1375 """Returns the build description of the system.
1300 1376
1301 For example: 1377 For example:
1302 nakasi-user 4.4.4 KTU84P 1227136 release-keys 1378 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1303 """ 1379 """
1304 return self.GetProp('ro.build.description', cache=True) 1380 return self.GetProp('ro.build.description', cache=True)
1305 1381
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
1626 @classmethod 1702 @classmethod
1627 def HealthyDevices(cls): 1703 def HealthyDevices(cls):
1628 blacklist = device_blacklist.ReadBlacklist() 1704 blacklist = device_blacklist.ReadBlacklist()
1629 def blacklisted(adb): 1705 def blacklisted(adb):
1630 if adb.GetDeviceSerial() in blacklist: 1706 if adb.GetDeviceSerial() in blacklist:
1631 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) 1707 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1632 return True 1708 return True
1633 return False 1709 return False
1634 1710
1635 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() 1711 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1636 if not blacklisted(adb)] 1712 if not blacklisted(adb)]
1637
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698