• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.core import exceptions
18from telemetry.internal.browser import extension_page
19
20_ARC_SUPPORT_HOST_URL = 'chrome-extension://cnbgggchhmkkdmeppjobngjoejnihlei/'
21_ARC_SUPPORT_HOST_PAGENAME = '_generated_background_page.html'
22_DUMPSTATE_DEFAULT_TIMEOUT = 20
23_DUMPSTATE_PATH = '/var/log/arc-dumpstate.log'
24_DUMPSTATE_PIPE_PATH = '/var/run/arc/bugreport/pipe'
25_USERNAME = 'arcplusplustest@gmail.com'
26_USERNAME_DISPLAY = 'arcplusplustest@gmail.com'
27_ARCP_URL = 'https://sites.google.com/a/chromium.org/dev/chromium-os' \
28                '/testing/arcplusplus-testing/arcp'
29_OPT_IN_BEGIN = 'Initializing ARC opt-in flow.'
30_OPT_IN_FINISH = 'ARC opt-in flow complete.'
31
32def should_start_arc(arc_mode):
33    """
34    Determines whether ARC should be started.
35
36    @param arc_mode: mode as defined in arc_common.
37
38    @returns: True or False.
39
40    """
41    logging.debug('ARC is enabled in mode ' + str(arc_mode))
42    assert arc_mode is None or arc_mode in arc_common.ARC_MODES
43    return arc_mode in [arc_common.ARC_MODE_ENABLED,
44                        arc_common.ARC_MODE_ENABLED_ASYNC]
45
46
47def get_extra_chrome_flags():
48    """Returns extra Chrome flags for ARC tests to run"""
49    return ['--disable-arc-opt-in-verification']
50
51
52def post_processing_after_browser(chrome):
53    """
54    Called when a new browser instance has been initialized.
55
56    Note that this hook function is called regardless of arc_mode.
57
58    @param chrome: Chrome object.
59
60    """
61    # Remove any stale dumpstate files.
62    if os.path.isfile(_DUMPSTATE_PATH):
63        os.unlink(_DUMPSTATE_PATH)
64
65    # Wait for Android container ready if ARC is enabled.
66    if chrome.arc_mode == arc_common.ARC_MODE_ENABLED:
67        try:
68            arc_common.wait_for_android_boot()
69        except Exception:
70            # Save dumpstate so that we can figure out why boot does not
71            # complete.
72            _save_android_dumpstate()
73            raise
74
75
76def pre_processing_before_close(chrome):
77    """
78    Called when the browser instance is being closed.
79
80    Note that this hook function is called regardless of arc_mode.
81
82    @param chrome: Chrome object.
83
84    """
85    if not should_start_arc(chrome.arc_mode):
86        return
87    # TODO(b/29341443): Implement stopping of adb logcat when we start adb
88    # logcat for all tests
89
90    # Save dumpstate just before logout.
91    _save_android_dumpstate()
92
93
94def _save_android_dumpstate(timeout=_DUMPSTATE_DEFAULT_TIMEOUT):
95    """
96    Triggers a dumpstate and saves its contents to to /var/log/arc-dumpstate.log
97    with logging.
98
99    Exception thrown while doing dumpstate will be ignored.
100
101    @param timeout: The timeout in seconds.
102    """
103
104    try:
105        logging.info('Saving Android dumpstate.')
106        with open(_DUMPSTATE_PATH, 'w') as out:
107            # _DUMPSTATE_PIPE_PATH is a named pipe, so it permanently blocks if
108            # opened normally if the other end has not been opened. In order to
109            # avoid that, open the file with O_NONBLOCK and use a select loop to
110            # read from the file with a timeout.
111            fd = os.open(_DUMPSTATE_PIPE_PATH, os.O_RDONLY | os.O_NONBLOCK)
112            with os.fdopen(fd, 'r') as pipe:
113                end_time = time.time() + timeout
114                while True:
115                    remaining_time = end_time - time.time()
116                    if remaining_time <= 0:
117                        break
118                    rlist, _, _ = select.select([pipe], [], [], remaining_time)
119                    if pipe not in rlist:
120                        break
121                    buf = os.read(pipe.fileno(), 1024)
122                    if len(buf) == 0:
123                        break
124                    out.write(buf)
125        logging.info('Android dumpstate successfully saved.')
126    except Exception:
127        # Dumpstate is nice-to-have stuff. Do not make it as a fatal error.
128        logging.exception('Failed to save Android dumpstate.')
129
130
131def set_browser_options_for_opt_in(b_options):
132    """
133    Setup Chrome for gaia login and opt_in.
134
135    @param b_options: browser options object used by chrome.Chrome.
136
137    """
138    b_options.username = _USERNAME
139    with tempfile.NamedTemporaryFile() as pltp:
140        file_utils.download_file(_ARCP_URL, pltp.name)
141        b_options.password = pltp.read().rstrip()
142    b_options.disable_default_apps = False
143    b_options.disable_component_extensions_with_background_pages = False
144    b_options.gaia_login = True
145
146
147def enable_play_store(autotest_ext, enabled):
148    """
149    Enable ARC++ Play Store
150
151    Do nothing if the account is managed.
152
153    @param autotest_ext: autotest extension object.
154
155    @param enabled: if True then perform opt-in, otherwise opt-out.
156
157    @returns: True if the opt-in should continue; else False.
158
159    """
160
161    if autotest_ext is None:
162         raise error.TestFail(
163                 'Could not change the Play Store enabled state because '
164                 'autotest API does not exist')
165
166    # Skip enabling for managed users, since value is policy enforced.
167    # Return early if a managed user has ArcEnabled set to false.
168    try:
169        autotest_ext.ExecuteJavaScript('''
170            chrome.autotestPrivate.getPlayStoreState(function(state) {
171              window.__play_store_state = state;
172            });
173        ''')
174        # Results must be available by the next invocation.
175        is_managed = autotest_ext.EvaluateJavaScript(
176            'window.__play_store_state.managed')
177        if is_managed:
178            logging.info('Determined that ARC is managed by user policy.')
179            policy_enabled = autotest_ext.EvaluateJavaScript(
180                'window.__play_store_state.enabled')
181            if enabled != policy_enabled:
182                logging.info(
183                    'Returning early since ARC is policy-enforced.')
184                return False
185        else:
186            autotest_ext.ExecuteJavaScript('''
187                    chrome.autotestPrivate.setPlayStoreEnabled(
188                        %s, function(enabled) {});
189                ''' % ('true' if enabled else 'false'))
190    except exceptions.EvaluateException as e:
191        raise error.TestFail('Could not change the Play Store enabled state '
192                             ' via autotest API. "%s".' % e)
193
194    return True
195
196
197def find_opt_in_extension_page(browser):
198    """
199    Find and verify the opt-in extension extension page.
200
201    @param browser: chrome.Chrome broswer object.
202
203    @returns: the extension page.
204
205    @raises: error.TestFail if extension is not found or is mal-formed.
206
207    """
208    opt_in_extension_id = extension_page.UrlToExtensionId(_ARC_SUPPORT_HOST_URL)
209    try:
210        extension_pages = browser.extensions.GetByExtensionId(
211            opt_in_extension_id)
212    except Exception, e:
213        raise error.TestFail('Could not locate extension for arc opt-in. '
214                             'Make sure disable_default_apps is False. '
215                             '"%s".' % e)
216
217    extension_main_page = None
218    for page in extension_pages:
219        url = page.EvaluateJavaScript('location.href;')
220        if url.endswith(_ARC_SUPPORT_HOST_PAGENAME):
221            extension_main_page = page
222            break
223    if not extension_main_page:
224        raise error.TestError('Found opt-in extension but not correct page!')
225    extension_main_page.WaitForDocumentReadyStateToBeComplete()
226
227    js_code_did_start_conditions = ['termsPage != null',
228            '(termsPage.isManaged_ || termsPage.state_ == LoadState.LOADED)']
229    try:
230        for condition in js_code_did_start_conditions:
231            extension_main_page.WaitForJavaScriptCondition(condition,
232                                                           timeout=60)
233    except Exception, e:
234        raise error.TestError('Error waiting for "%s": "%s".' % (condition, e))
235
236    return extension_main_page
237
238
239def opt_in_and_wait_for_completion(extension_main_page):
240    """
241    Step through the user input of the opt-in extension and wait for completion.
242
243    @param extension_main_page: opt-in extension object.
244
245    @raises error.TestFail if opt-in doesn't complete after timeout.
246
247    """
248    extension_main_page.ExecuteJavaScript('termsPage.onAgree()')
249
250    SIGN_IN_TIMEOUT = 120
251    try:
252        extension_main_page.WaitForJavaScriptCondition('!appWindow',
253                                                       timeout=SIGN_IN_TIMEOUT)
254    except Exception, e:
255        js_read_error_message = """
256            err = appWindow.contentWindow.document.getElementById(
257                    "error-message");
258            if (err) {
259                err.innerText;
260            }
261        """
262        err_msg = extension_main_page.EvaluateJavaScript(js_read_error_message)
263        err_msg = err_msg.strip()
264        logging.error('Error: %s', err_msg.strip())
265        if err_msg:
266            raise error.TestFail('Opt-in app error: %s' % err_msg)
267        else:
268            raise error.TestFail('Opt-in app did not finish running after %s '
269                                 'seconds!' % SIGN_IN_TIMEOUT)
270    # Reset termsPage to be able to reuse OptIn page and wait condition for ToS
271    # are loaded.
272    extension_main_page.ExecuteJavaScript('termsPage = null')
273
274
275def opt_in(browser, autotest_ext):
276    """
277    Step through opt in and wait for it to complete.
278
279    Return early if the arc_setting cannot be set True.
280
281    @param browser: chrome.Chrome browser object.
282    @param autotest_ext: autotest extension object.
283
284    @raises: error.TestFail if opt in fails.
285
286    """
287
288    logging.info(_OPT_IN_BEGIN)
289    if not enable_play_store(autotest_ext, True):
290        return
291
292    extension_main_page = find_opt_in_extension_page(browser)
293    opt_in_and_wait_for_completion(extension_main_page)
294    logging.info(_OPT_IN_FINISH)
295