arc_util.py revision ef2eaccbed2a30105e3b6efdeb0640923c5390f1
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.common_lib import error 15from autotest_lib.client.common_lib import file_utils 16from autotest_lib.client.common_lib.cros import arc_common 17from telemetry.internal.browser import extension_page 18 19_ARC_SUPPORT_HOST_URL = 'chrome-extension://cnbgggchhmkkdmeppjobngjoejnihlei/' 20_ARC_SUPPORT_HOST_PAGENAME = '_generated_background_page.html' 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 enable_arc_setting(browser): 137 """ 138 Enable ARC++ via the settings page checkbox. 139 140 Do nothing if the account is managed. 141 142 @param browser: chrome.Chrome broswer object. 143 144 @returns: True if the opt-in should continue; else False. 145 146 """ 147 settings_tab = browser.tabs.New() 148 149 try: 150 settings_tab.Navigate('chrome://settings-frame') 151 settings_tab.WaitForDocumentReadyStateToBeComplete() 152 153 try: 154 settings_tab.ExecuteJavaScript( 155 'assert(document.getElementById("android-apps-enabled"))') 156 except Exception, e: 157 raise error.TestFail('Could not locate section in chrome://settings' 158 ' to enable arc. Make sure ARC is available.') 159 160 # Skip enabling for managed users, since value is policy enforced. 161 # Return early if a managed user has ArcEnabled set to false. 162 is_managed = settings_tab.EvaluateJavaScript( 163 'document.getElementById("android-apps-enabled").disabled') 164 if is_managed: 165 logging.info('Determined that ARC is managed by user policy.') 166 policy_value = settings_tab.EvaluateJavaScript( 167 'document.getElementById("android-apps-enabled").checked') 168 if not policy_value: 169 logging.info( 170 'Returning early since ARC is policy-enforced off.') 171 return False 172 else: 173 settings_tab.ExecuteJavaScript( 174 'Preferences.setBooleanPref("arc.enabled", true, true)') 175 finally: 176 settings_tab.Close() 177 178 return True 179 180 181def find_opt_in_extension_page(browser): 182 """ 183 Find and verify the opt-in extension extension page. 184 185 @param browser: chrome.Chrome broswer object. 186 187 @returns: the extension page. 188 189 @raises: error.TestFail if extension is not found or is mal-formed. 190 191 """ 192 opt_in_extension_id = extension_page.UrlToExtensionId(_ARC_SUPPORT_HOST_URL) 193 try: 194 extension_pages = browser.extensions.GetByExtensionId( 195 opt_in_extension_id) 196 except Exception, e: 197 raise error.TestFail('Could not locate extension for arc opt-in. ' 198 'Make sure disable_default_apps is False. ' 199 '"%s".' % e) 200 201 extension_main_page = None 202 for page in extension_pages: 203 url = page.EvaluateJavaScript('location.href;') 204 if url.endswith(_ARC_SUPPORT_HOST_PAGENAME): 205 extension_main_page = page 206 break 207 if not extension_main_page: 208 raise error.TestError('Found opt-in extension but not correct page!') 209 extension_main_page.WaitForDocumentReadyStateToBeComplete() 210 211 js_code_did_start_conditions = ['termsPage != null', 212 '(termsPage.isManaged_ || termsPage.state_ == LoadState.LOADED)'] 213 try: 214 for condition in js_code_did_start_conditions: 215 extension_main_page.WaitForJavaScriptCondition(condition, 216 timeout=60) 217 except Exception, e: 218 raise error.TestError('Error waiting for "%s": "%s".' % (condition, e)) 219 220 return extension_main_page 221 222 223def opt_in_and_wait_for_completion(extension_main_page): 224 """ 225 Step through the user input of the opt-in extension and wait for completion. 226 227 @param extension_main_page: opt-in extension object. 228 229 @raises error.TestFail if opt-in doesn't complete after timeout. 230 231 """ 232 js_code_click_agree = """ 233 doc = appWindow.contentWindow.document; 234 agree_button_element = doc.getElementById('button-agree'); 235 agree_button_element.click(); 236 """ 237 extension_main_page.ExecuteJavaScript(js_code_click_agree) 238 239 SIGN_IN_TIMEOUT = 120 240 try: 241 extension_main_page.WaitForJavaScriptCondition('!appWindow', 242 timeout=SIGN_IN_TIMEOUT) 243 except Exception, e: 244 js_read_error_message = """ 245 err = appWindow.contentWindow.document.getElementById( 246 "error-message"); 247 if (err) { 248 err.innerText; 249 } 250 """ 251 err_msg = extension_main_page.EvaluateJavaScript(js_read_error_message) 252 err_msg = err_msg.strip() 253 logging.error('Error: %s', err_msg.strip()) 254 if err_msg: 255 raise error.TestFail('Opt-in app error: %s' % err_msg) 256 else: 257 raise error.TestFail('Opt-in app did not finish running after %s ' 258 'seconds!' % SIGN_IN_TIMEOUT) 259 260 261def opt_in(browser): 262 """ 263 Step through opt in and wait for it to complete. 264 265 Return early if the arc_setting cannot be set True. 266 267 @param browser: chrome.Chrome broswer object. 268 269 @raises: error.TestFail if opt in fails. 270 271 """ 272 logging.info(_OPT_IN_BEGIN) 273 if not enable_arc_setting(browser): 274 return 275 extension_main_page = find_opt_in_extension_page(browser) 276 opt_in_and_wait_for_completion(extension_main_page) 277 logging.info(_OPT_IN_FINISH) 278