# Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json import logging import time from autotest_lib.client.bin import test from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.cros import chrome from autotest_lib.client.cros import cryptohome from autotest_lib.client.cros.input_playback import input_playback class desktopui_CheckRlzPingSent(test.test): """Tests creating a new user, doing a google search, checking RLZ Data.""" version = 1 _RLZ_DATA_FILE = "/home/chronos/user/RLZ Data" def _verify_rlz_data(self, expect_caf_ping=True, guest=False): """ Checks the RLZ data file for CAI and CAF ping events. @param expect_caf_ping: True if expecting the CAF event to be in the RLZ data file, False if not expecting it. @param guest: True if checking in guest mode. The guest mode user mount may not be in the root mount namespace, so the RLZ data file path must be adjusted accordingly. """ rlz_data_cmd = [] if guest: mounter_pid = utils.run( ['pgrep', '-f', '/usr/sbin/cryptohome-namespace-mount']).stdout if mounter_pid is not None: ns_path = '/proc/%s/ns/mnt' % mounter_pid.rstrip() rlz_data_cmd.extend(['nsenter', '--mount=%s' % ns_path]) rlz_data_cmd.extend(['cat', self._RLZ_DATA_FILE]) def rlz_data_exists(): """Check rlz data exists.""" rlz_data = json.loads(utils.run(rlz_data_cmd).stdout) logging.debug('rlz data: %s', rlz_data) if 'stateful_events' in rlz_data: cai_present = 'CAI' in rlz_data['stateful_events']['C']['_'] caf_present = 'CAF' in rlz_data['stateful_events']['C']['_'] return cai_present and (caf_present == expect_caf_ping) return False utils.poll_for_condition(rlz_data_exists, timeout=120) def _check_url_for_rlz(self, cr): """ Does a Google search and ensures there is an rlz parameter. @param cr: Chrome instance. """ timeout_minutes = 2 timeout = time.time() + 60 * timeout_minutes # Setup a keyboard emulator to open new tabs and type a search. with input_playback.InputPlayback() as player: player.emulate(input_type='keyboard') player.find_connected_inputs() while True: # Open a new tab, search in the omnibox. player.blocking_playback_of_default_file( input_type='keyboard', filename='keyboard_ctrl+t') player.blocking_playback_of_default_file( input_type='keyboard', filename='keyboard_b+a+d+enter') logging.info(cr.browser.tabs[-1].url) if 'rlz=' in cr.browser.tabs[-1].url: break else: if time.time() > timeout: raise error.TestFail('RLZ ping did not send in %d ' 'minutes.' % timeout_minutes) time.sleep(10) def _wait_for_rlz_lock(self): """Waits for the DUT to get into locked state after login.""" def get_install_lockbox_finalized_status(): status = cryptohome.get_install_attribute_status() return status == 'VALID' try: utils.poll_for_condition( lambda: get_install_lockbox_finalized_status(), exception=utils.TimeoutError(), timeout=120) except utils.TimeoutError: raise error.TestFail('Timed out trying to lock the device') def run_once(self, ping_timeout=30, expect_caf_ping=True, username=None, pre_login=None, pre_login_username=None): """ Tests whether or not the RLZ install event (CAI) and first-use event (CAF) pings are sent. After the first user login, the CAI ping will be sent after a certain delay. This delay is 24 hours by default, but can be overridden by specifying the rlz-ping-delay flag in /etc/chrome_dev.conf, or by using the --rlz-ping-delay argument to Chrome. Then, if two RW_VPD settings have the correct values (should_send_rlz_ping == 1, rlz_embargo_end_date has passed OR not specified), the CAF ping will be sent as well. Ping status is checked in the /home/chronos/user/'RLZ Data' file, which will contain entries for CAI and CAF pings in the 'stateful_events' section. @param ping_timeout: Delay time (seconds) before any RLZ pings are sent. @param expect_caf_ping: True if expecting the first-use event (CAF) ping to be sent, False if not expecting it. The ping would not be expected if the relevant RW_VPD settings do not have the right combination of values. @param username: Username to log in with during the main RLZ check. None to sign in with the default test user account. Specifying a username will log in with a profile distinct from the test user. @param pre_login: Whether or not to login before the main RLZ ping test, and for how long. Should be one of ['lock', 'no_lock', None]. 'lock' is meant for guest mode testing, where a non-guest user must login to 'lock' the device for RLZ before the ping can be sent in guest mode. 'no_lock' is to log and log out immediately to ensure no ping is sent. Used to verify that the ping can be sent from subsequent user logins if it has not already been sent. @param pre_login_username: The username to sign in with for the pre-login step. None to use the default test user account. """ # Browser arg to make DUT send rlz ping after a short delay. browser_args = ['--rlz-ping-delay=%d' % ping_timeout] # TODO(crbug/1103298): keyboard input doesn't work in guest mode # without disabling this flag. Remove when bug is fixed. if pre_login == 'lock': browser_args.append('--disable-features=ImeInputLogicFst') # If we are testing the ping is sent in guest mode (pre_login='lock'), # we need to first do a real login and wait for the DUT to become # 'locked' for rlz. Then logout and enter guest mode. # If we are testing the ping can be sent by the second user to use the # device, we will login and immediately logout (pre_login='no_lock'). if pre_login is not None: logging.debug("Logging in before main RLZ test with username " "flag: %s", pre_login_username) with chrome.Chrome(logged_in=True, username=pre_login_username, extra_browser_args=browser_args): if pre_login is 'lock': logging.debug("Waiting for device to be 'locked' for RLZ") self._wait_for_rlz_lock() logging.debug("Starting RLZ check with username flag: %s", username) # Pass clear_enterprise_policy=False in guest mode to avoid deleting # /home/chronos/'Local State' between logins. Deleting it will cause # the guest mode test to fail on boards that do not have rlz_brand_code # in the VPD (mainly unibuild models). This file is normally not # deleted between logins anyways. with chrome.Chrome( logged_in=pre_login is not 'lock', clear_enterprise_policy=pre_login is not 'lock', extra_browser_args=browser_args, username=username, dont_override_profile=True) as cr: self._check_url_for_rlz(cr) self._verify_rlz_data(expect_caf_ping=expect_caf_ping, guest=pre_login is 'lock')