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

Side by Side Diff: devil/devil/android/device_utils.py

Issue 2973293002: [devil] Set permissions on install and gracefully handle errors (Closed)
Patch Set: single adb shell call Created 3 years, 5 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
« no previous file with comments | « no previous file | devil/devil/android/device_utils_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
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
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
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
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
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)
OLDNEW
« no previous file with comments | « no previous file | devil/devil/android/device_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698