1# Lint as: python2, python3
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import json
7import logging
8import time
9
10from autotest_lib.client.bin import test
11from autotest_lib.client.bin import utils
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.common_lib.cros import chrome
14from autotest_lib.client.cros import cryptohome
15from autotest_lib.client.cros.input_playback import input_playback
16
17
18class desktopui_CheckRlzPingSent(test.test):
19    """Tests creating a new user, doing a google search, checking RLZ Data."""
20    version = 1
21
22    _RLZ_DATA_FILE = "/home/chronos/user/RLZ Data"
23
24    def _verify_rlz_data(self, expect_caf_ping=True, guest=False):
25        """
26        Checks the RLZ data file for CAI and CAF ping events.
27
28        @param expect_caf_ping: True if expecting the CAF event to be in the
29                                RLZ data file, False if not expecting it.
30        @param guest: True if checking in guest mode. The guest mode user
31                      mount may not be in the root mount namespace, so the RLZ
32                      data file path must be adjusted accordingly.
33
34        """
35        rlz_data_cmd = []
36        if guest:
37            mounter_pid = utils.run(
38                ['pgrep', '-f', '/usr/sbin/cryptohome-namespace-mount']).stdout
39            if mounter_pid is not None:
40                ns_path = '/proc/%s/ns/mnt' % mounter_pid.rstrip()
41                rlz_data_cmd.extend(['nsenter', '--mount=%s' % ns_path])
42
43        rlz_data_cmd.extend(['cat', self._RLZ_DATA_FILE])
44
45        def rlz_data_exists():
46            """Check rlz data exists."""
47            rlz_data = json.loads(utils.run(rlz_data_cmd).stdout)
48            logging.debug('rlz data: %s', rlz_data)
49            if 'stateful_events' in rlz_data:
50                cai_present = 'CAI' in rlz_data['stateful_events']['C']['_']
51                caf_present = 'CAF' in rlz_data['stateful_events']['C']['_']
52                return cai_present and (caf_present == expect_caf_ping)
53            return False
54
55        utils.poll_for_condition(rlz_data_exists, timeout=120)
56
57
58    def _check_url_for_rlz(self, cr):
59        """
60        Does a Google search and ensures there is an rlz parameter.
61
62        @param cr: Chrome instance.
63
64        """
65        timeout_minutes = 2
66        timeout = time.time() + 60 * timeout_minutes
67
68        # Setup a keyboard emulator to open new tabs and type a search.
69        with input_playback.InputPlayback() as player:
70            player.emulate(input_type='keyboard')
71            player.find_connected_inputs()
72
73            while True:
74                # Open a new tab, search in the omnibox.
75                player.blocking_playback_of_default_file(
76                    input_type='keyboard', filename='keyboard_ctrl+t')
77                player.blocking_playback_of_default_file(
78                    input_type='keyboard', filename='keyboard_b+a+d+enter')
79                logging.info(cr.browser.tabs[-1].url)
80                if 'rlz=' in cr.browser.tabs[-1].url:
81                    break
82                else:
83                    if time.time() > timeout:
84                        raise error.TestFail('RLZ ping did not send in %d '
85                                             'minutes.' % timeout_minutes)
86                    time.sleep(10)
87
88
89    def _wait_for_rlz_lock(self):
90        """Waits for the DUT to get into locked state after login."""
91        def get_install_lockbox_finalized_status():
92            status = cryptohome.get_install_attribute_status()
93            return status == 'VALID'
94
95        try:
96            utils.poll_for_condition(
97                lambda: get_install_lockbox_finalized_status(),
98                exception=utils.TimeoutError(),
99                timeout=120)
100        except utils.TimeoutError:
101            raise error.TestFail('Timed out trying to lock the device')
102
103
104    def run_once(self, ping_timeout=30, expect_caf_ping=True, username=None,
105                 pre_login=None, pre_login_username=None):
106        """
107        Tests whether or not the RLZ install event (CAI) and first-use event
108        (CAF) pings are sent. After the first user login, the CAI ping will
109        be sent after a certain delay. This delay is 24 hours by default, but
110        can be overridden by specifying the rlz-ping-delay flag in
111        /etc/chrome_dev.conf, or by using the --rlz-ping-delay argument to
112        Chrome. Then, if two RW_VPD settings have the correct values
113        (should_send_rlz_ping == 1, rlz_embargo_end_date has passed OR not
114        specified), the CAF ping will be sent as well. Ping status is checked
115        in the /home/chronos/user/'RLZ Data' file, which will contain entries
116        for CAI and CAF pings in the 'stateful_events' section.
117
118        @param ping_timeout: Delay time (seconds) before any RLZ pings are
119                             sent.
120        @param expect_caf_ping: True if expecting the first-use event (CAF)
121                                ping to be sent, False if not expecting it.
122                                The ping would not be expected if the relevant
123                                RW_VPD settings do not have the right
124                                combination of values.
125        @param username: Username to log in with during the main RLZ check.
126                         None to sign in with the default test user account.
127                         Specifying a username will log in with a profile
128                         distinct from the test user.
129        @param pre_login: Whether or not to login before the main RLZ ping
130                          test, and for how long. Should be one of
131                          ['lock', 'no_lock', None]. 'lock' is meant for guest
132                          mode testing, where a non-guest user must login to
133                          'lock' the device for RLZ before the ping can be
134                          sent in guest mode. 'no_lock' is to log and log out
135                          immediately to ensure no ping is sent. Used to
136                          verify that the ping can be sent from subsequent
137                          user logins if it has not already been sent.
138        @param pre_login_username: The username to sign in with for the
139                                   pre-login step. None to use the default
140                                   test user account.
141
142        """
143        # Browser arg to make DUT send rlz ping after a short delay.
144        browser_args = ['--rlz-ping-delay=%d' % ping_timeout]
145
146        # TODO(crbug/1103298): keyboard input doesn't work in guest mode
147        # without disabling this flag. Remove when bug is fixed.
148        if pre_login == 'lock':
149            browser_args.append('--disable-features=ImeInputLogicFst')
150
151        # If we are testing the ping is sent in guest mode (pre_login='lock'),
152        # we need to first do a real login and wait for the DUT to become
153        # 'locked' for rlz. Then logout and enter guest mode.
154        # If we are testing the ping can be sent by the second user to use the
155        # device, we will login and immediately logout (pre_login='no_lock').
156        if pre_login is not None:
157            logging.debug("Logging in before main RLZ test with username "
158                          "flag: %s", pre_login_username)
159            with chrome.Chrome(logged_in=True, username=pre_login_username,
160                               extra_browser_args=browser_args):
161                if pre_login is 'lock':
162                    logging.debug("Waiting for device to be 'locked' for RLZ")
163                    self._wait_for_rlz_lock()
164
165        logging.debug("Starting RLZ check with username flag: %s", username)
166        # Pass clear_enterprise_policy=False in guest mode to avoid deleting
167        # /home/chronos/'Local State' between logins. Deleting it will cause
168        # the guest mode test to fail on boards that do not have rlz_brand_code
169        # in the VPD (mainly unibuild models). This file is normally not
170        # deleted between logins anyways.
171        with chrome.Chrome(
172                logged_in=pre_login is not 'lock',
173                clear_enterprise_policy=pre_login is not 'lock',
174                extra_browser_args=browser_args,
175                username=username,
176                dont_override_profile=True) as cr:
177            self._check_url_for_rlz(cr)
178            self._verify_rlz_data(expect_caf_ping=expect_caf_ping,
179                                  guest=pre_login is 'lock')
180