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]) | 130 _PERMISSIONS_EXCEPTION_RE = re.compile( |
| 131 r'java\.lang\.\w+Exception: .*$', re.MULTILINE) |
129 | 132 |
130 _CURRENT_FOCUS_CRASH_RE = re.compile( | 133 _CURRENT_FOCUS_CRASH_RE = re.compile( |
131 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') | 134 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') |
132 | 135 |
133 _GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') | 136 _GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') |
134 | 137 |
135 # Regex to parse the long (-l) output of 'ls' command, c.f. | 138 # 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 | 139 # https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446 |
137 _LONG_LS_OUTPUT_RE = re.compile( | 140 _LONG_LS_OUTPUT_RE = re.compile( |
138 r'(?P<st_mode>[\w-]{10})\s+' # File permissions | 141 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) | 844 self._cache['package_apk_paths'].pop(package_name, 0) |
842 self._cache['package_apk_checksums'].pop(package_name, 0) | 845 self._cache['package_apk_checksums'].pop(package_name, 0) |
843 if split_apks: | 846 if split_apks: |
844 partial = package_name if len(apks_to_install) < len(all_apks) else None | 847 partial = package_name if len(apks_to_install) < len(all_apks) else None |
845 self.adb.InstallMultiple( | 848 self.adb.InstallMultiple( |
846 apks_to_install, partial=partial, reinstall=reinstall, | 849 apks_to_install, partial=partial, reinstall=reinstall, |
847 allow_downgrade=allow_downgrade) | 850 allow_downgrade=allow_downgrade) |
848 else: | 851 else: |
849 self.adb.Install( | 852 self.adb.Install( |
850 base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade) | 853 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: | 854 else: |
859 # Running adb install terminates running instances of the app, so to be | 855 # Running adb install terminates running instances of the app, so to be |
860 # consistent, we explicitly terminate it when skipping the install. | 856 # consistent, we explicitly terminate it when skipping the install. |
861 self.ForceStop(package_name) | 857 self.ForceStop(package_name) |
862 | 858 |
| 859 if (permissions is None |
| 860 and self.build_version_sdk >= version_codes.MARSHMALLOW): |
| 861 permissions = base_apk.GetPermissions() |
| 862 self.GrantPermissions(package_name, permissions) |
| 863 # Upon success, we know the device checksums, but not their paths. |
| 864 if host_checksums is not None: |
| 865 self._cache['package_apk_checksums'][package_name] = host_checksums |
| 866 |
863 @decorators.WithTimeoutAndRetriesFromInstance() | 867 @decorators.WithTimeoutAndRetriesFromInstance() |
864 def Uninstall(self, package_name, keep_data=False, timeout=None, | 868 def Uninstall(self, package_name, keep_data=False, timeout=None, |
865 retries=None): | 869 retries=None): |
866 """Remove the app |package_name| from the device. | 870 """Remove the app |package_name| from the device. |
867 | 871 |
868 This is a no-op if the app is not already installed. | 872 This is a no-op if the app is not already installed. |
869 | 873 |
870 Args: | 874 Args: |
871 package_name: The package to uninstall. | 875 package_name: The package to uninstall. |
872 keep_data: (optional) Whether to keep the data and cache directories. | 876 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( | 2657 self.RunShellCommand( |
2654 ['source', script.name], check_return=True, as_root=True) | 2658 ['source', script.name], check_return=True, as_root=True) |
2655 self.adb.WaitForDevice() | 2659 self.adb.WaitForDevice() |
2656 | 2660 |
2657 @decorators.WithTimeoutAndRetriesFromInstance() | 2661 @decorators.WithTimeoutAndRetriesFromInstance() |
2658 def GrantPermissions(self, package, permissions, timeout=None, retries=None): | 2662 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 | 2663 # Permissions only need to be set on M and above because of the changes to |
2660 # the permission model. | 2664 # the permission model. |
2661 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: | 2665 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: |
2662 return | 2666 return |
2663 logger.info('Setting permissions for %s.', package) | 2667 |
2664 permissions = [p for p in permissions if p not in _PERMISSIONS_BLACKLIST] | 2668 permissions = set( |
| 2669 p for p in permissions if not _PERMISSIONS_BLACKLIST_RE.match(p)) |
| 2670 |
2665 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions | 2671 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions |
2666 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): | 2672 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): |
2667 permissions.append('android.permission.READ_EXTERNAL_STORAGE') | 2673 permissions.add('android.permission.READ_EXTERNAL_STORAGE') |
2668 cmd = '&&'.join('pm grant %s %s' % (package, p) for p in permissions) | 2674 |
2669 if cmd: | 2675 script = ';'.join([ |
2670 output = self.RunShellCommand(cmd, shell=True, check_return=True) | 2676 'p={package}', |
2671 if output: | 2677 'for q in {permissions}', |
2672 logger.warning('Possible problem when granting permissions. Blacklist ' | 2678 'do pm grant "$p" "$q"', |
2673 'may need to be updated.') | 2679 'echo "{sep}$q{sep}$?{sep}"', |
2674 for line in output: | 2680 'done' |
2675 logger.warning(' %s', line) | 2681 ]).format( |
| 2682 package=cmd_helper.SingleQuote(package), |
| 2683 permissions=' '.join( |
| 2684 cmd_helper.SingleQuote(p) for p in sorted(permissions)), |
| 2685 sep=_SHELL_OUTPUT_SEPARATOR) |
| 2686 |
| 2687 logger.info('Setting permissions for %s.', package) |
| 2688 res = self.RunShellCommand( |
| 2689 script, shell=True, raw_output=True, large_output=True, |
| 2690 check_return=True) |
| 2691 res = res.split(_SHELL_OUTPUT_SEPARATOR) |
| 2692 failures = [ |
| 2693 (permission, output.strip()) |
| 2694 for permission, status, output in zip(res[1::3], res[2::3], res[0::3]) |
| 2695 if int(status)] |
| 2696 |
| 2697 if failures: |
| 2698 logger.warning( |
| 2699 'Failed to grant some permissions. Blacklist may need to be updated?') |
| 2700 for permission, output in failures: |
| 2701 # Try to grab the relevant error message from the output. |
| 2702 m = _PERMISSIONS_EXCEPTION_RE.search(output) |
| 2703 if m: |
| 2704 error_msg = m.group(0) |
| 2705 elif len(output) > 200: |
| 2706 error_msg = repr(output[:200]) + ' (truncated)' |
| 2707 else: |
| 2708 error_msg = repr(output) |
| 2709 logger.warning('- %s: %s', permission, error_msg) |
2676 | 2710 |
2677 @decorators.WithTimeoutAndRetriesFromInstance() | 2711 @decorators.WithTimeoutAndRetriesFromInstance() |
2678 def IsScreenOn(self, timeout=None, retries=None): | 2712 def IsScreenOn(self, timeout=None, retries=None): |
2679 """Determines if screen is on. | 2713 """Determines if screen is on. |
2680 | 2714 |
2681 Dumpsys input_method exposes screen on/off state. Below is an explination of | 2715 Dumpsys input_method exposes screen on/off state. Below is an explination of |
2682 the states. | 2716 the states. |
2683 | 2717 |
2684 Pre-L: | 2718 Pre-L: |
2685 On: mScreenOn=true | 2719 On: mScreenOn=true |
(...skipping 29 matching lines...) Expand all Loading... |
2715 on: bool to decide state to switch to. True = on False = off. | 2749 on: bool to decide state to switch to. True = on False = off. |
2716 """ | 2750 """ |
2717 def screen_test(): | 2751 def screen_test(): |
2718 return self.IsScreenOn() == on | 2752 return self.IsScreenOn() == on |
2719 | 2753 |
2720 if screen_test(): | 2754 if screen_test(): |
2721 logger.info('Screen already in expected state.') | 2755 logger.info('Screen already in expected state.') |
2722 return | 2756 return |
2723 self.SendKeyEvent(keyevent.KEYCODE_POWER) | 2757 self.SendKeyEvent(keyevent.KEYCODE_POWER) |
2724 timeout_retry.WaitFor(screen_test, wait_period=1) | 2758 timeout_retry.WaitFor(screen_test, wait_period=1) |
OLD | NEW |