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

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

Issue 2973293002: [devil] Set permissions on install and gracefully handle errors (Closed)
Patch Set: small fix 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]) 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
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
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
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)
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