arc_util.py revision d33c21d5d384efdb53643bf1168b17628905ac68
1# Copyright 2016 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4# 5# arc_util.py is supposed to be called from chrome.py for ARC specific logic. 6# It should not import arc.py since it will create a import loop. 7 8import logging 9import os 10import select 11import tempfile 12import time 13 14from autotest_lib.client.bin import utils 15from autotest_lib.client.common_lib import error 16from autotest_lib.client.common_lib import file_utils 17from autotest_lib.client.common_lib.cros import arc_common 18from telemetry.internal.browser import extension_page 19 20_ARC_SUPPORT_HOST_URL = 'chrome-extension://cnbgggchhmkkdmeppjobngjoejnihlei/' 21_DUMPSTATE_DEFAULT_TIMEOUT = 20 22_DUMPSTATE_PATH = '/var/log/arc-dumpstate.log' 23_DUMPSTATE_PIPE_PATH = '/var/run/arc/bugreport/pipe' 24_USERNAME = 'powerloadtest@gmail.com' 25_USERNAME_DISPLAY = 'power.loadtest@gmail.com' 26_PLTP_URL = 'https://sites.google.com/a/chromium.org/dev/chromium-os' \ 27 '/testing/power-testing/pltp/pltp' 28_OPT_IN_BEGIN = 'Initializing ARC opt-in flow.' 29_OPT_IN_FINISH = 'ARC opt-in flow complete.' 30 31def should_start_arc(arc_mode): 32 """ 33 Determines whether ARC should be started. 34 35 @param arc_mode: mode as defined in arc_common. 36 37 @returns: True or False. 38 39 """ 40 logging.debug('ARC is enabled in mode ' + str(arc_mode)) 41 assert arc_mode is None or arc_mode in arc_common.ARC_MODES 42 return arc_mode in [arc_common.ARC_MODE_ENABLED, 43 arc_common.ARC_MODE_ENABLED_ASYNC] 44 45 46def get_extra_chrome_flags(): 47 """Returns extra Chrome flags for ARC tests to run""" 48 return ['--disable-arc-opt-in-verification'] 49 50 51def post_processing_after_browser(chrome): 52 """ 53 Called when a new browser instance has been initialized. 54 55 Note that this hook function is called regardless of arc_mode. 56 57 @param chrome: Chrome object. 58 59 """ 60 # Wait for Android container ready if ARC is enabled. 61 if chrome.arc_mode == arc_common.ARC_MODE_ENABLED: 62 arc_common.wait_for_android_boot() 63 # Remove any stale dumpstate files. 64 if os.path.isfile(_DUMPSTATE_PATH): 65 os.unlink(_DUMPSTATE_PATH) 66 67 68def pre_processing_before_close(chrome): 69 """ 70 Called when the browser instance is being closed. 71 72 Note that this hook function is called regardless of arc_mode. 73 74 @param chrome: Chrome object. 75 76 """ 77 if not should_start_arc(chrome.arc_mode): 78 return 79 # TODO(b/29341443): Implement stopping of adb logcat when we start adb 80 # logcat for all tests 81 82 # Save dumpstate just before logout. 83 try: 84 logging.info('Saving Android dumpstate.') 85 _save_android_dumpstate() 86 logging.info('Android dumpstate successfully saved.') 87 except Exception: 88 # Dumpstate is nice-to-have stuff. Do not make it as a fatal error. 89 logging.exception('Failed to save Android dumpstate.') 90 91 92def _save_android_dumpstate(timeout=_DUMPSTATE_DEFAULT_TIMEOUT): 93 """ 94 Triggers a dumpstate and saves its contents to to /var/log/arc-dumpstate.log 95 96 @param timeout: The timeout in seconds. 97 """ 98 99 with open(_DUMPSTATE_PATH, 'w') as out: 100 # _DUMPSTATE_PIPE_PATH is a named pipe, so it permanently blocks if 101 # opened normally if the other end has not been opened. In order to 102 # avoid that, open the file with O_NONBLOCK and use a select loop to 103 # read from the file with a timeout. 104 fd = os.open(_DUMPSTATE_PIPE_PATH, os.O_RDONLY | os.O_NONBLOCK) 105 with os.fdopen(fd, 'r') as pipe: 106 end_time = time.time() + timeout 107 while True: 108 remaining_time = end_time - time.time() 109 if remaining_time <= 0: 110 break 111 rlist, _, _ = select.select([pipe], [], [], remaining_time) 112 if pipe not in rlist: 113 break 114 buf = os.read(pipe.fileno(), 1024) 115 if len(buf) == 0: 116 break 117 out.write(buf) 118 119 120def set_browser_options_for_opt_in(b_options): 121 """ 122 Setup Chrome for gaia login and opt_in. 123 124 @param b_options: browser options object used by chrome.Chrome. 125 126 """ 127 b_options.username = _USERNAME 128 with tempfile.NamedTemporaryFile() as pltp: 129 file_utils.download_file(_PLTP_URL, pltp.name) 130 b_options.password = pltp.read().rstrip() 131 b_options.disable_default_apps = False 132 b_options.disable_component_extensions_with_background_pages = False 133 b_options.gaia_login = True 134 135 136def opt_in(browser): 137 """ 138 Step through opt in and wait for it to complete. 139 140 @param browser: chrome.Chrome broswer object. 141 142 @raises: error.TestFail if opt in fails. 143 144 """ 145 logging.info(_OPT_IN_BEGIN) 146 147 opt_in_extension_id = extension_page.UrlToExtensionId(_ARC_SUPPORT_HOST_URL) 148 try: 149 extension_main_page = browser.extensions.GetByExtensionId( 150 opt_in_extension_id)[0] 151 except Exception, e: 152 raise error.TestFail('Could not locate extension for arc opt-in.' + 153 'Make sure disable_default_apps is False.') 154 155 settings_tab = browser.tabs[0] 156 settings_tab.Navigate('chrome://settings') 157 settings_tab.WaitForDocumentReadyStateToBeComplete() 158 159 try: 160 js_code_assert_arc_option_available = """ 161 assert(document.getElementById('android-apps-enabled')); 162 """ 163 settings_tab.ExecuteJavaScript(js_code_assert_arc_option_available) 164 except Exception, e: 165 raise error.TestFail('Could not locate section in chrome://settings' + 166 ' to enable arc. Make sure arc is available.') 167 168 # Skip enabling for managed users, since value is policy enforced. 169 # Return early if a managed user has ArcEnabled set to false. 170 js_code_is_managed = ('document.getElementById(' 171 '"android-apps-enabled").disabled') 172 is_managed = settings_tab.EvaluateJavaScript(js_code_is_managed) 173 if is_managed: 174 logging.info('Determined that ARC++ is managed by user policy.') 175 js_code_policy_value = ('document.getElementById(' 176 '"android-apps-enabled").checked') 177 policy_value = settings_tab.EvaluateJavaScript(js_code_policy_value) 178 if not policy_value: 179 logging.info('Returning early since ARC++ is policy enforced off.') 180 return 181 else: 182 js_code_enable_arc = ('Preferences.setBooleanPref(\'arc.enabled\', ' 183 'true, true)') 184 settings_tab.ExecuteJavaScript(js_code_enable_arc) 185 186 js_code_did_start_conditions = ['appWindow', 'termsView', 187 ('!appWindow.contentWindow.document' 188 '.getElementById(\'terms\').hidden')] 189 190 extension_main_page.WaitForDocumentReadyStateToBeComplete() 191 for condition in js_code_did_start_conditions: 192 extension_main_page.WaitForJavaScriptExpression(condition, 60.0) 193 194 js_code_click_agree = """ 195 doc = appWindow.contentWindow.document; 196 agree_button_element = doc.getElementById('button-agree'); 197 agree_button_element.click(); 198 """ 199 extension_main_page.ExecuteJavaScript(js_code_click_agree) 200 201 js_code_is_lso_section_active = """ 202 !appWindow.contentWindow.document.getElementById('lso').hidden 203 """ 204 try: 205 extension_main_page.WaitForJavaScriptExpression( 206 js_code_is_lso_section_active, 120) 207 except Exception, e: 208 raise error.TestFail('Error occured while waiting for lso session. This' + 209 'may have been caused if gaia login was not used.') 210 211 web_views = utils.poll_for_condition( 212 extension_main_page.GetWebviewContexts, timeout=60, 213 exception=error.TestError('WebviewContexts error during opt in!')) 214 215 js_code_is_sign_in_button_enabled = """ 216 !document.getElementById('submit_approve_access') 217 .hasAttribute('disabled') 218 """ 219 web_views[0].WaitForJavaScriptExpression( 220 js_code_is_sign_in_button_enabled, 60.0) 221 222 js_code_click_sign_in = """ 223 sign_in_button_element = document.getElementById('submit_approve_access'); 224 sign_in_button_element.click(); 225 """ 226 web_views[0].ExecuteJavaScript(js_code_click_sign_in) 227 228 # Wait for app to close (i.e. complete sign in). 229 SIGN_IN_TIMEOUT = 120 230 try: 231 extension_main_page.WaitForJavaScriptExpression('!appWindow', 232 SIGN_IN_TIMEOUT) 233 except Exception, e: 234 js_read_error_message = """ 235 err = appWindow.contentWindow.document.getElementById( 236 "error-message"); 237 if (err) { 238 err.innerText; 239 } 240 """ 241 err_msg = extension_main_page.EvaluateJavaScript(js_read_error_message) 242 err_msg = err_msg.strip() 243 logging.error('Error: %s', err_msg.strip()) 244 if err_msg: 245 raise error.TestFail('Opt-in app error: %s' % err_msg) 246 else: 247 raise error.TestFail('Opt-in app did not finish running after %s ' 248 'seconds!' % SIGN_IN_TIMEOUT) 249 250 logging.info(_OPT_IN_FINISH) 251