OLD | NEW |
---|---|
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 |
11 import calendar | 11 import calendar |
12 import collections | 12 import collections |
13 import fnmatch | |
13 import itertools | 14 import itertools |
14 import json | 15 import json |
15 import logging | 16 import logging |
16 import multiprocessing | 17 import multiprocessing |
17 import os | 18 import os |
18 import posixpath | 19 import posixpath |
19 import pprint | 20 import pprint |
20 import random | 21 import random |
21 import re | 22 import re |
22 import shutil | 23 import shutil |
23 import stat | 24 import stat |
24 import tempfile | 25 import tempfile |
25 import time | 26 import time |
26 import threading | 27 import threading |
27 import uuid | 28 import uuid |
28 import zipfile | 29 import zipfile |
29 | 30 |
30 from devil import base_error | 31 from devil import base_error |
31 from devil import devil_env | 32 from devil import devil_env |
32 from devil.utils import cmd_helper | 33 from devil.utils import cmd_helper |
33 from devil.android import apk_helper | 34 from devil.android import apk_helper |
34 from devil.android import device_signal | 35 from devil.android import device_signal |
35 from devil.android import decorators | 36 from devil.android import decorators |
36 from devil.android import device_errors | 37 from devil.android import device_errors |
37 from devil.android import device_temp_file | 38 from devil.android import device_temp_file |
38 from devil.android import install_commands | 39 from devil.android import install_commands |
39 from devil.android import logcat_monitor | 40 from devil.android import logcat_monitor |
40 from devil.android import md5sum | 41 from devil.android import md5sum |
41 from devil.android.constants import chrome | |
42 from devil.android.sdk import adb_wrapper | 42 from devil.android.sdk import adb_wrapper |
43 from devil.android.sdk import intent | 43 from devil.android.sdk import intent |
44 from devil.android.sdk import keyevent | 44 from devil.android.sdk import keyevent |
45 from devil.android.sdk import split_select | 45 from devil.android.sdk import split_select |
46 from devil.android.sdk import version_codes | 46 from devil.android.sdk import version_codes |
47 from devil.utils import host_utils | 47 from devil.utils import host_utils |
48 from devil.utils import parallelizer | 48 from devil.utils import parallelizer |
49 from devil.utils import reraiser_thread | 49 from devil.utils import reraiser_thread |
50 from devil.utils import timeout_retry | 50 from devil.utils import timeout_retry |
51 from devil.utils import zip_utils | 51 from devil.utils import zip_utils |
(...skipping 13 matching lines...) Expand all Loading... | |
65 trap '' TERM | 65 trap '' TERM |
66 trap '' PIPE | 66 trap '' PIPE |
67 function restart() { | 67 function restart() { |
68 stop adbd | 68 stop adbd |
69 start adbd | 69 start adbd |
70 } | 70 } |
71 restart & | 71 restart & |
72 """ | 72 """ |
73 | 73 |
74 # Not all permissions can be set. | 74 # Not all permissions can be set. |
75 _PERMISSIONS_BLACKLIST = [ | 75 _PERMISSIONS_BLACKLIST_RE = re.compile('|'.join(fnmatch.translate(p) for p in [ |
76 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS', | 76 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS', |
77 'android.permission.ACCESS_MOCK_LOCATION', | 77 'android.permission.ACCESS_MOCK_LOCATION', |
78 'android.permission.ACCESS_NETWORK_STATE', | 78 'android.permission.ACCESS_NETWORK_STATE', |
79 'android.permission.ACCESS_NOTIFICATION_POLICY', | 79 'android.permission.ACCESS_NOTIFICATION_POLICY', |
80 'android.permission.ACCESS_WIFI_STATE', | 80 'android.permission.ACCESS_WIFI_STATE', |
81 'android.permission.AUTHENTICATE_ACCOUNTS', | 81 'android.permission.AUTHENTICATE_ACCOUNTS', |
82 'android.permission.BLUETOOTH', | 82 'android.permission.BLUETOOTH', |
83 'android.permission.BLUETOOTH_ADMIN', | 83 'android.permission.BLUETOOTH_ADMIN', |
84 'android.permission.BROADCAST_STICKY', | 84 'android.permission.BROADCAST_STICKY', |
85 'android.permission.CHANGE_NETWORK_STATE', | 85 'android.permission.CHANGE_NETWORK_STATE', |
86 'android.permission.CHANGE_WIFI_MULTICAST_STATE', | 86 'android.permission.CHANGE_WIFI_MULTICAST_STATE', |
87 'android.permission.CHANGE_WIFI_STATE', | 87 'android.permission.CHANGE_WIFI_STATE', |
88 'android.permission.DISABLE_KEYGUARD', | 88 'android.permission.DISABLE_KEYGUARD', |
89 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION', | 89 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION', |
90 'android.permission.EXPAND_STATUS_BAR', | 90 'android.permission.EXPAND_STATUS_BAR', |
91 'android.permission.GET_PACKAGE_SIZE', | 91 'android.permission.GET_PACKAGE_SIZE', |
92 'android.permission.INSTALL_SHORTCUT', | 92 'android.permission.INSTALL_SHORTCUT', |
93 'android.permission.INJECT_EVENTS', | |
93 'android.permission.INTERNET', | 94 'android.permission.INTERNET', |
94 'android.permission.KILL_BACKGROUND_PROCESSES', | 95 'android.permission.KILL_BACKGROUND_PROCESSES', |
95 'android.permission.MANAGE_ACCOUNTS', | 96 'android.permission.MANAGE_ACCOUNTS', |
96 'android.permission.MODIFY_AUDIO_SETTINGS', | 97 'android.permission.MODIFY_AUDIO_SETTINGS', |
97 'android.permission.NFC', | 98 'android.permission.NFC', |
98 'android.permission.READ_SYNC_SETTINGS', | 99 'android.permission.READ_SYNC_SETTINGS', |
99 'android.permission.READ_SYNC_STATS', | 100 'android.permission.READ_SYNC_STATS', |
100 'android.permission.RECEIVE_BOOT_COMPLETED', | 101 'android.permission.RECEIVE_BOOT_COMPLETED', |
101 'android.permission.RECORD_VIDEO', | 102 'android.permission.RECORD_VIDEO', |
102 'android.permission.REORDER_TASKS', | 103 'android.permission.REORDER_TASKS', |
103 'android.permission.REQUEST_INSTALL_PACKAGES', | 104 'android.permission.REQUEST_INSTALL_PACKAGES', |
105 'android.permission.RESTRICTED_VR_ACCESS', | |
104 'android.permission.RUN_INSTRUMENTATION', | 106 'android.permission.RUN_INSTRUMENTATION', |
105 'android.permission.SET_ALARM', | 107 'android.permission.SET_ALARM', |
106 'android.permission.SET_TIME_ZONE', | 108 'android.permission.SET_TIME_ZONE', |
107 'android.permission.SET_WALLPAPER', | 109 'android.permission.SET_WALLPAPER', |
108 'android.permission.SET_WALLPAPER_HINTS', | 110 'android.permission.SET_WALLPAPER_HINTS', |
109 'android.permission.TRANSMIT_IR', | 111 'android.permission.TRANSMIT_IR', |
110 'android.permission.USE_CREDENTIALS', | 112 'android.permission.USE_CREDENTIALS', |
111 'android.permission.USE_FINGERPRINT', | 113 'android.permission.USE_FINGERPRINT', |
112 'android.permission.VIBRATE', | 114 'android.permission.VIBRATE', |
113 'android.permission.WAKE_LOCK', | 115 'android.permission.WAKE_LOCK', |
114 'android.permission.WRITE_SYNC_SETTINGS', | 116 'android.permission.WRITE_SYNC_SETTINGS', |
115 'com.android.browser.permission.READ_HISTORY_BOOKMARKS', | 117 'com.android.browser.permission.READ_HISTORY_BOOKMARKS', |
116 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS', | 118 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS', |
117 'com.android.launcher.permission.INSTALL_SHORTCUT', | 119 'com.android.launcher.permission.INSTALL_SHORTCUT', |
118 'com.chrome.permission.DEVICE_EXTRAS', | 120 'com.chrome.permission.DEVICE_EXTRAS', |
119 'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS', | 121 'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS', |
120 'com.google.android.c2dm.permission.RECEIVE', | 122 'com.google.android.c2dm.permission.RECEIVE', |
121 'com.google.android.providers.gsf.permission.READ_GSERVICES', | 123 'com.google.android.providers.gsf.permission.READ_GSERVICES', |
122 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER', | 124 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER', |
123 ] | 125 '*.permission.C2D_MESSAGE', |
124 for package_info in chrome.PACKAGE_INFO.itervalues(): | 126 '*.permission.READ_WRITE_BOOKMARK_FOLDERS', |
125 _PERMISSIONS_BLACKLIST.extend([ | 127 '*.TOS_ACKED', |
126 '%s.permission.C2D_MESSAGE' % package_info.package, | 128 ])) |
127 '%s.permission.READ_WRITE_BOOKMARK_FOLDERS' % package_info.package, | 129 _SHELL_OUTPUT_SEPARATOR = '~X~' |
128 '%s.TOS_ACKED' % package_info.package]) | |
129 | 130 |
130 _CURRENT_FOCUS_CRASH_RE = re.compile( | 131 _CURRENT_FOCUS_CRASH_RE = re.compile( |
131 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') | 132 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') |
132 | 133 |
133 _GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') | 134 _GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') |
134 | 135 |
135 # Regex to parse the long (-l) output of 'ls' command, c.f. | 136 # Regex to parse the long (-l) output of 'ls' command, c.f. |
136 # https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446 | 137 # https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446 |
137 _LONG_LS_OUTPUT_RE = re.compile( | 138 _LONG_LS_OUTPUT_RE = re.compile( |
138 r'(?P<st_mode>[\w-]{10})\s+' # File permissions | 139 r'(?P<st_mode>[\w-]{10})\s+' # File permissions |
(...skipping 702 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
841 self._cache['package_apk_paths'].pop(package_name, 0) | 842 self._cache['package_apk_paths'].pop(package_name, 0) |
842 self._cache['package_apk_checksums'].pop(package_name, 0) | 843 self._cache['package_apk_checksums'].pop(package_name, 0) |
843 if split_apks: | 844 if split_apks: |
844 partial = package_name if len(apks_to_install) < len(all_apks) else None | 845 partial = package_name if len(apks_to_install) < len(all_apks) else None |
845 self.adb.InstallMultiple( | 846 self.adb.InstallMultiple( |
846 apks_to_install, partial=partial, reinstall=reinstall, | 847 apks_to_install, partial=partial, reinstall=reinstall, |
847 allow_downgrade=allow_downgrade) | 848 allow_downgrade=allow_downgrade) |
848 else: | 849 else: |
849 self.adb.Install( | 850 self.adb.Install( |
850 base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade) | 851 base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade) |
851 if (permissions is None | |
852 and self.build_version_sdk >= version_codes.MARSHMALLOW): | |
853 permissions = base_apk.GetPermissions() | |
854 self.GrantPermissions(package_name, permissions) | |
855 # Upon success, we know the device checksums, but not their paths. | |
856 if host_checksums is not None: | |
857 self._cache['package_apk_checksums'][package_name] = host_checksums | |
858 else: | 852 else: |
859 # Running adb install terminates running instances of the app, so to be | 853 # Running adb install terminates running instances of the app, so to be |
860 # consistent, we explicitly terminate it when skipping the install. | 854 # consistent, we explicitly terminate it when skipping the install. |
861 self.ForceStop(package_name) | 855 self.ForceStop(package_name) |
862 | 856 |
857 if (permissions is None | |
858 and self.build_version_sdk >= version_codes.MARSHMALLOW): | |
859 permissions = base_apk.GetPermissions() | |
860 self.GrantPermissions(package_name, permissions) | |
861 # Upon success, we know the device checksums, but not their paths. | |
862 if host_checksums is not None: | |
863 self._cache['package_apk_checksums'][package_name] = host_checksums | |
864 | |
863 @decorators.WithTimeoutAndRetriesFromInstance() | 865 @decorators.WithTimeoutAndRetriesFromInstance() |
864 def Uninstall(self, package_name, keep_data=False, timeout=None, | 866 def Uninstall(self, package_name, keep_data=False, timeout=None, |
865 retries=None): | 867 retries=None): |
866 """Remove the app |package_name| from the device. | 868 """Remove the app |package_name| from the device. |
867 | 869 |
868 This is a no-op if the app is not already installed. | 870 This is a no-op if the app is not already installed. |
869 | 871 |
870 Args: | 872 Args: |
871 package_name: The package to uninstall. | 873 package_name: The package to uninstall. |
872 keep_data: (optional) Whether to keep the data and cache directories. | 874 keep_data: (optional) Whether to keep the data and cache directories. |
(...skipping 1780 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2653 self.RunShellCommand( | 2655 self.RunShellCommand( |
2654 ['source', script.name], check_return=True, as_root=True) | 2656 ['source', script.name], check_return=True, as_root=True) |
2655 self.adb.WaitForDevice() | 2657 self.adb.WaitForDevice() |
2656 | 2658 |
2657 @decorators.WithTimeoutAndRetriesFromInstance() | 2659 @decorators.WithTimeoutAndRetriesFromInstance() |
2658 def GrantPermissions(self, package, permissions, timeout=None, retries=None): | 2660 def GrantPermissions(self, package, permissions, timeout=None, retries=None): |
2659 # Permissions only need to be set on M and above because of the changes to | 2661 # Permissions only need to be set on M and above because of the changes to |
2660 # the permission model. | 2662 # the permission model. |
2661 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: | 2663 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: |
2662 return | 2664 return |
2663 logger.info('Setting permissions for %s.', package) | 2665 |
2664 permissions = [p for p in permissions if p not in _PERMISSIONS_BLACKLIST] | 2666 permissions = set( |
2667 p for p in permissions if not _PERMISSIONS_BLACKLIST_RE.match(p)) | |
2668 | |
2665 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions | 2669 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions |
2666 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): | 2670 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): |
2667 permissions.append('android.permission.READ_EXTERNAL_STORAGE') | 2671 permissions.add('android.permission.READ_EXTERNAL_STORAGE') |
2668 cmd = '&&'.join('pm grant %s %s' % (package, p) for p in permissions) | 2672 |
2669 if cmd: | 2673 script = ';'.join([ |
2670 output = self.RunShellCommand(cmd, shell=True, check_return=True) | 2674 'p={package}', |
2671 if output: | 2675 'for q in {permissions}', |
2672 logger.warning('Possible problem when granting permissions. Blacklist ' | 2676 'do pm grant "$p" "$q"', |
2673 'may need to be updated.') | 2677 'echo "{sep}$q{sep}$?{sep}"', |
2674 for line in output: | 2678 'done' |
2675 logger.warning(' %s', line) | 2679 ]).format( |
2680 package=cmd_helper.SingleQuote(package), | |
2681 permissions=' '.join( | |
2682 cmd_helper.SingleQuote(p) for p in sorted(permissions)), | |
2683 sep=_SHELL_OUTPUT_SEPARATOR) | |
2684 | |
2685 logger.info('Setting permissions for %s.', package) | |
2686 res = self.RunShellCommand( | |
jbudorick
2017/07/17 15:31:38
nit: might be worth using large_output=True here,
perezju
2017/07/17 16:08:38
Thought about that, but needs another fix. Current
| |
2687 script, shell=True, raw_output=True, check_return=True) | |
2688 res = res.split(_SHELL_OUTPUT_SEPARATOR) | |
2689 failures = [ | |
2690 (permission, output.strip()) | |
2691 for permission, status, output in zip(res[1::3], res[2::3], res[0::3]) | |
2692 if int(status)] | |
2693 | |
2694 if failures: | |
2695 logger.warning( | |
2696 'Failed to grant some permissions. Blacklist may need to be updated?') | |
2697 for permission, output in failures: | |
2698 # Try to grab the relevant error message from the output. | |
2699 m = re.search(r'java\.lang\.\w+Exception: .*$', output, re.MULTILINE) | |
jbudorick
2017/07/17 15:31:38
nit: compile the regex as a module-scope constant
perezju
2017/07/17 16:08:38
Done.
| |
2700 if m: | |
2701 error_msg = m.group(0) | |
2702 elif len(output) > 200: | |
2703 error_msg = repr(output[:200]) + ' (truncated)' | |
2704 else: | |
2705 error_msg = repr(output) | |
2706 logger.warning('- %s: %s', permission, error_msg) | |
2676 | 2707 |
2677 @decorators.WithTimeoutAndRetriesFromInstance() | 2708 @decorators.WithTimeoutAndRetriesFromInstance() |
2678 def IsScreenOn(self, timeout=None, retries=None): | 2709 def IsScreenOn(self, timeout=None, retries=None): |
2679 """Determines if screen is on. | 2710 """Determines if screen is on. |
2680 | 2711 |
2681 Dumpsys input_method exposes screen on/off state. Below is an explination of | 2712 Dumpsys input_method exposes screen on/off state. Below is an explination of |
2682 the states. | 2713 the states. |
2683 | 2714 |
2684 Pre-L: | 2715 Pre-L: |
2685 On: mScreenOn=true | 2716 On: mScreenOn=true |
(...skipping 29 matching lines...) Expand all Loading... | |
2715 on: bool to decide state to switch to. True = on False = off. | 2746 on: bool to decide state to switch to. True = on False = off. |
2716 """ | 2747 """ |
2717 def screen_test(): | 2748 def screen_test(): |
2718 return self.IsScreenOn() == on | 2749 return self.IsScreenOn() == on |
2719 | 2750 |
2720 if screen_test(): | 2751 if screen_test(): |
2721 logger.info('Screen already in expected state.') | 2752 logger.info('Screen already in expected state.') |
2722 return | 2753 return |
2723 self.SendKeyEvent(keyevent.KEYCODE_POWER) | 2754 self.SendKeyEvent(keyevent.KEYCODE_POWER) |
2724 timeout_retry.WaitFor(screen_test, wait_period=1) | 2755 timeout_retry.WaitFor(screen_test, wait_period=1) |
OLD | NEW |