xref: /aosp_15_r20/external/autotest/client/common_lib/cros/arc.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Lifrom __future__ import absolute_import
7*9c5db199SXin Lifrom __future__ import division
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Li
10*9c5db199SXin Liimport collections
11*9c5db199SXin Liimport glob
12*9c5db199SXin Liimport logging
13*9c5db199SXin Liimport os
14*9c5db199SXin Liimport pipes
15*9c5db199SXin Liimport shutil
16*9c5db199SXin Liimport socket
17*9c5db199SXin Liimport sys
18*9c5db199SXin Liimport tempfile
19*9c5db199SXin Liimport time
20*9c5db199SXin Li
21*9c5db199SXin Lifrom autotest_lib.client.bin import test, utils
22*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
23*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import chrome, arc_common
24*9c5db199SXin Lifrom six.moves import range
25*9c5db199SXin Li
26*9c5db199SXin Li_ADB_KEYS_PATH = '/tmp/adb_keys'
27*9c5db199SXin Li_ADB_VENDOR_KEYS = 'ADB_VENDOR_KEYS'
28*9c5db199SXin Li_ANDROID_CONTAINER_PID_PATH = '/run/containers/android*/container.pid'
29*9c5db199SXin Li_ANDROID_DATA_ROOT_PATH = '/opt/google/containers/android/rootfs/android-data'
30*9c5db199SXin Li_ANDROID_CONTAINER_ROOT_PATH = '/opt/google/containers/android/rootfs'
31*9c5db199SXin Li_SCREENSHOT_DIR_PATH = '/var/log/arc-screenshots'
32*9c5db199SXin Li_SCREENSHOT_BASENAME = 'arc-screenshot'
33*9c5db199SXin Li_MAX_SCREENSHOT_NUM = 10
34*9c5db199SXin Li# This address should match the one present in
35*9c5db199SXin Li# https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/main/chromeos-base/arc-sslh-init/files/sslh.conf
36*9c5db199SXin Li_ADBD_ADDRESS = ('100.115.92.2', 5555)
37*9c5db199SXin Li_ADBD_PID_PATH = '/run/arc/adbd.pid'
38*9c5db199SXin Li_SDCARD_PID_PATH = '/run/arc/sdcard.pid'
39*9c5db199SXin Li_ANDROID_ADB_KEYS_PATH = '/data/misc/adb/adb_keys'
40*9c5db199SXin Li_PROCESS_CHECK_INTERVAL_SECONDS = 1
41*9c5db199SXin Li_PROPERTY_CHECK_INTERVAL_SECONDS = 1
42*9c5db199SXin Li_WAIT_FOR_ADB_READY = 60
43*9c5db199SXin Li_WAIT_FOR_ANDROID_PROCESS_SECONDS = 60
44*9c5db199SXin Li_PLAY_STORE_PKG = 'com.android.vending'
45*9c5db199SXin Li_SETTINGS_PKG = 'com.android.settings'
46*9c5db199SXin Li
47*9c5db199SXin Li
48*9c5db199SXin Lidef setup_adb_host():
49*9c5db199SXin Li    """Setup ADB host keys.
50*9c5db199SXin Li
51*9c5db199SXin Li    This sets up the files and environment variables that wait_for_adb_ready()
52*9c5db199SXin Li    needs"""
53*9c5db199SXin Li    if _ADB_VENDOR_KEYS in os.environ:
54*9c5db199SXin Li        return
55*9c5db199SXin Li    if not os.path.exists(_ADB_KEYS_PATH):
56*9c5db199SXin Li        os.mkdir(_ADB_KEYS_PATH)
57*9c5db199SXin Li    # adb expects $HOME to be writable.
58*9c5db199SXin Li    os.environ['HOME'] = _ADB_KEYS_PATH
59*9c5db199SXin Li
60*9c5db199SXin Li    # Generate and save keys for adb if needed
61*9c5db199SXin Li    key_path = os.path.join(_ADB_KEYS_PATH, 'test_key')
62*9c5db199SXin Li    if not os.path.exists(key_path):
63*9c5db199SXin Li        utils.system('adb keygen ' + pipes.quote(key_path))
64*9c5db199SXin Li    os.environ[_ADB_VENDOR_KEYS] = key_path
65*9c5db199SXin Li
66*9c5db199SXin Li
67*9c5db199SXin Lidef restart_adbd(timeout):
68*9c5db199SXin Li    """Restarts the adb daemon.
69*9c5db199SXin Li
70*9c5db199SXin Li    Follows the same logic as tast.
71*9c5db199SXin Li    """
72*9c5db199SXin Li    logging.debug('restarting adbd')
73*9c5db199SXin Li    config = 'adb'
74*9c5db199SXin Li    _android_shell('setprop persist.sys.usb.config ' + config)
75*9c5db199SXin Li    _android_shell('setprop sys.usb.config ' + config)
76*9c5db199SXin Li
77*9c5db199SXin Li    def property_check():
78*9c5db199SXin Li        return _android_shell('getprop sys.usb.state') == config
79*9c5db199SXin Li
80*9c5db199SXin Li    try:
81*9c5db199SXin Li        utils.poll_for_condition(
82*9c5db199SXin Li                condition=property_check,
83*9c5db199SXin Li                desc='Wait for sys.usb.state',
84*9c5db199SXin Li                timeout=timeout,
85*9c5db199SXin Li                sleep_interval=_PROPERTY_CHECK_INTERVAL_SECONDS)
86*9c5db199SXin Li    except utils.TimeoutError:
87*9c5db199SXin Li        raise error.TestFail('Timed out waiting for sys.usb.state change')
88*9c5db199SXin Li
89*9c5db199SXin Li    _android_shell('setprop ctl.restart adbd')
90*9c5db199SXin Li
91*9c5db199SXin Li
92*9c5db199SXin Lidef restart_adb():
93*9c5db199SXin Li    """Restarts adb.
94*9c5db199SXin Li
95*9c5db199SXin Li    Follows the same logic as in tast, specifically avoiding kill-server
96*9c5db199SXin Li    since it is unreliable (crbug.com/855325).
97*9c5db199SXin Li    """
98*9c5db199SXin Li    logging.debug('killing and restarting adb server')
99*9c5db199SXin Li    utils.system('killall --quiet --wait -KILL adb')
100*9c5db199SXin Li    utils.system('adb start-server')
101*9c5db199SXin Li
102*9c5db199SXin Li
103*9c5db199SXin Lidef is_adb_connected():
104*9c5db199SXin Li    """Return true if adb is connected to the container."""
105*9c5db199SXin Li    output = utils.system_output('adb get-state', ignore_status=True)
106*9c5db199SXin Li    logging.debug('adb get-state: %s', output)
107*9c5db199SXin Li    return output.strip() == 'device'
108*9c5db199SXin Li
109*9c5db199SXin Li
110*9c5db199SXin Lidef _is_android_data_mounted():
111*9c5db199SXin Li    """Return true if Android's /data is mounted with partial boot enabled."""
112*9c5db199SXin Li    return _android_shell('getprop ro.data_mounted', ignore_status=True) == '1'
113*9c5db199SXin Li
114*9c5db199SXin Li
115*9c5db199SXin Lidef get_zygote_type():
116*9c5db199SXin Li    """Return zygote service type."""
117*9c5db199SXin Li    return _android_shell('getprop ro.zygote', ignore_status=True)
118*9c5db199SXin Li
119*9c5db199SXin Li
120*9c5db199SXin Lidef get_sdk_version():
121*9c5db199SXin Li    """Return the SDK level version for Android."""
122*9c5db199SXin Li    return _android_shell('getprop ro.build.version.sdk')
123*9c5db199SXin Li
124*9c5db199SXin Li
125*9c5db199SXin Lidef get_product():
126*9c5db199SXin Li    """Return the product string used for the Android build."""
127*9c5db199SXin Li    return _android_shell('getprop ro.build.product', ignore_status=True)
128*9c5db199SXin Li
129*9c5db199SXin Li
130*9c5db199SXin Lidef _is_tcp_port_reachable(address):
131*9c5db199SXin Li    """Return whether a TCP port described by |address| is reachable."""
132*9c5db199SXin Li    try:
133*9c5db199SXin Li        s = socket.create_connection(address)
134*9c5db199SXin Li        s.close()
135*9c5db199SXin Li        return True
136*9c5db199SXin Li    except socket.error:
137*9c5db199SXin Li        return False
138*9c5db199SXin Li
139*9c5db199SXin Li
140*9c5db199SXin Lidef _wait_for_data_mounted(timeout):
141*9c5db199SXin Li    utils.poll_for_condition(
142*9c5db199SXin Li            condition=_is_android_data_mounted,
143*9c5db199SXin Li            desc='Wait for /data mounted',
144*9c5db199SXin Li            timeout=timeout,
145*9c5db199SXin Li            sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS)
146*9c5db199SXin Li
147*9c5db199SXin Li
148*9c5db199SXin Lidef wait_for_adb_ready(timeout=_WAIT_FOR_ADB_READY):
149*9c5db199SXin Li    """Wait for the ADB client to connect to the ARC container.
150*9c5db199SXin Li
151*9c5db199SXin Li    @param timeout: Timeout in seconds.
152*9c5db199SXin Li    """
153*9c5db199SXin Li    # Although adbd is started at login screen, we still need /data to be
154*9c5db199SXin Li    # mounted to set up key-based authentication. /data should be mounted
155*9c5db199SXin Li    # once the user has logged in.
156*9c5db199SXin Li
157*9c5db199SXin Li    initial_timeout = timeout
158*9c5db199SXin Li
159*9c5db199SXin Li    start_time = time.time()
160*9c5db199SXin Li    _wait_for_data_mounted(timeout)
161*9c5db199SXin Li    timeout -= (time.time() - start_time)
162*9c5db199SXin Li    start_time = time.time()
163*9c5db199SXin Li    arc_common.wait_for_android_boot(timeout)
164*9c5db199SXin Li    timeout -= (time.time() - start_time)
165*9c5db199SXin Li
166*9c5db199SXin Li    setup_adb_host()
167*9c5db199SXin Li    if is_adb_connected():
168*9c5db199SXin Li        return
169*9c5db199SXin Li
170*9c5db199SXin Li    # Push keys for adb.
171*9c5db199SXin Li    pubkey_path = os.environ[_ADB_VENDOR_KEYS] + '.pub'
172*9c5db199SXin Li    with open(pubkey_path, 'r') as f:
173*9c5db199SXin Li        _write_android_file(_ANDROID_ADB_KEYS_PATH, f.read())
174*9c5db199SXin Li    _android_shell('chown shell ' + pipes.quote(_ANDROID_ADB_KEYS_PATH))
175*9c5db199SXin Li    _android_shell('restorecon ' + pipes.quote(_ANDROID_ADB_KEYS_PATH))
176*9c5db199SXin Li
177*9c5db199SXin Li    attempt_count = 3
178*9c5db199SXin Li    timeout = timeout / attempt_count
179*9c5db199SXin Li
180*9c5db199SXin Li    for i in range(attempt_count):
181*9c5db199SXin Li        if _restart_adb_and_wait_for_ready(timeout):
182*9c5db199SXin Li            return
183*9c5db199SXin Li    raise error.TestFail(
184*9c5db199SXin Li            'Failed to connect to adb in %d seconds.' % initial_timeout)
185*9c5db199SXin Li
186*9c5db199SXin Li
187*9c5db199SXin Lidef _restart_adb_and_wait_for_ready(timeout):
188*9c5db199SXin Li    """Restart adb/adbd and wait adb connection is ready.
189*9c5db199SXin Li
190*9c5db199SXin Li    @param timeout: Timeout in seconds.
191*9c5db199SXin Li    @return True in case adb connection was established or throw an error in
192*9c5db199SXin Li            case persistent error occured.
193*9c5db199SXin Li    """
194*9c5db199SXin Li
195*9c5db199SXin Li    # Restart adbd and adb.
196*9c5db199SXin Li    start_time = time.time()
197*9c5db199SXin Li    restart_adbd(timeout)
198*9c5db199SXin Li    timeout -= (time.time() - start_time)
199*9c5db199SXin Li    start_time = time.time()
200*9c5db199SXin Li    restart_adb()
201*9c5db199SXin Li    timeout -= (time.time() - start_time)
202*9c5db199SXin Li
203*9c5db199SXin Li    try:
204*9c5db199SXin Li        utils.poll_for_condition(condition=is_adb_connected,
205*9c5db199SXin Li                                 timeout=timeout)
206*9c5db199SXin Li        return True
207*9c5db199SXin Li    except (utils.TimeoutError):
208*9c5db199SXin Li        # The operation has failed, but let's try to clarify the failure to
209*9c5db199SXin Li        # avoid shifting blame to adb.
210*9c5db199SXin Li
211*9c5db199SXin Li        # First, collect some information and log it.
212*9c5db199SXin Li        arc_alive = is_android_container_alive()
213*9c5db199SXin Li        arc_booted = _android_shell('getprop ro.arc.boot_completed',
214*9c5db199SXin Li                                    ignore_status=True)
215*9c5db199SXin Li        arc_system_events = _android_shell(
216*9c5db199SXin Li            'logcat -d -b events *:S arc_system_event', ignore_status=True)
217*9c5db199SXin Li        adbd_pid = _android_shell('pidof -s adbd', ignore_status=True)
218*9c5db199SXin Li        adbd_port_reachable = _is_tcp_port_reachable(_ADBD_ADDRESS)
219*9c5db199SXin Li        adb_state = utils.system_output('adb get-state', ignore_status=True)
220*9c5db199SXin Li        logging.debug('ARC alive: %s', arc_alive)
221*9c5db199SXin Li        logging.debug('ARC booted: %s', arc_booted)
222*9c5db199SXin Li        logging.debug('ARC system events: %s', arc_system_events)
223*9c5db199SXin Li        logging.debug('adbd process: %s', adbd_pid)
224*9c5db199SXin Li        logging.debug('adbd port reachable: %s', adbd_port_reachable)
225*9c5db199SXin Li        logging.debug('adb state: %s', adb_state)
226*9c5db199SXin Li
227*9c5db199SXin Li        # Now go through the usual suspects and raise nicer errors to make the
228*9c5db199SXin Li        # actual failure clearer.
229*9c5db199SXin Li        if not arc_alive:
230*9c5db199SXin Li            raise error.TestFail('ARC is not alive.')
231*9c5db199SXin Li        if arc_booted != '1':
232*9c5db199SXin Li            raise error.TestFail('ARC did not finish booting.')
233*9c5db199SXin Li        return False
234*9c5db199SXin Li
235*9c5db199SXin Li
236*9c5db199SXin Lidef grant_permissions(package, permissions):
237*9c5db199SXin Li    """Grants permissions to a package.
238*9c5db199SXin Li
239*9c5db199SXin Li    @param package: Package name.
240*9c5db199SXin Li    @param permissions: A list of permissions.
241*9c5db199SXin Li
242*9c5db199SXin Li    """
243*9c5db199SXin Li    for permission in permissions:
244*9c5db199SXin Li        adb_shell('pm grant %s android.permission.%s' % (
245*9c5db199SXin Li                  pipes.quote(package), pipes.quote(permission)))
246*9c5db199SXin Li
247*9c5db199SXin Li
248*9c5db199SXin Lidef adb_cmd(cmd, **kwargs):
249*9c5db199SXin Li    """Executed cmd using adb. Must wait for adb ready.
250*9c5db199SXin Li
251*9c5db199SXin Li    @param cmd: Command to run.
252*9c5db199SXin Li    """
253*9c5db199SXin Li    # TODO(b/79122489) - Assert if cmd == 'root'
254*9c5db199SXin Li    wait_for_adb_ready()
255*9c5db199SXin Li    return utils.system_output('adb %s' % cmd, **kwargs)
256*9c5db199SXin Li
257*9c5db199SXin Li
258*9c5db199SXin Lidef adb_shell(cmd, **kwargs):
259*9c5db199SXin Li    """Executed shell command with adb.
260*9c5db199SXin Li
261*9c5db199SXin Li    @param cmd: Command to run.
262*9c5db199SXin Li    """
263*9c5db199SXin Li    output = adb_cmd('shell %s' % pipes.quote(cmd), **kwargs)
264*9c5db199SXin Li    # Some android commands include a trailing CRLF in their output.
265*9c5db199SXin Li    if kwargs.pop('strip_trailing_whitespace', True):
266*9c5db199SXin Li        output = output.rstrip()
267*9c5db199SXin Li    return output
268*9c5db199SXin Li
269*9c5db199SXin Li
270*9c5db199SXin Lidef adb_install(apk, auto_grant_permissions=True, ignore_status=False):
271*9c5db199SXin Li    """Install an apk into container. You must connect first.
272*9c5db199SXin Li
273*9c5db199SXin Li    @param apk: Package to install.
274*9c5db199SXin Li    @param auto_grant_permissions: Set to false to not automatically grant all
275*9c5db199SXin Li    permissions. Most tests should not care.
276*9c5db199SXin Li    @param ignore_status: Set to true to allow the install command to fail,
277*9c5db199SXin Li    for example if you are installing multiple architectures and only need
278*9c5db199SXin Li    one to succeed.
279*9c5db199SXin Li    """
280*9c5db199SXin Li    flags = '-g' if auto_grant_permissions else ''
281*9c5db199SXin Li    return adb_cmd('install -r -t %s %s' % (flags, apk),
282*9c5db199SXin Li                   timeout=60*5,
283*9c5db199SXin Li                   ignore_status=ignore_status)
284*9c5db199SXin Li
285*9c5db199SXin Li
286*9c5db199SXin Lidef adb_uninstall(apk):
287*9c5db199SXin Li    """Remove an apk from container. You must connect first.
288*9c5db199SXin Li
289*9c5db199SXin Li    @param apk: Package to uninstall.
290*9c5db199SXin Li    """
291*9c5db199SXin Li    return adb_cmd('uninstall %s' % apk)
292*9c5db199SXin Li
293*9c5db199SXin Li
294*9c5db199SXin Lidef adb_reboot():
295*9c5db199SXin Li    """Reboots the container and block until container pid is gone.
296*9c5db199SXin Li
297*9c5db199SXin Li    You must connect first.
298*9c5db199SXin Li    """
299*9c5db199SXin Li    old_pid = get_container_pid()
300*9c5db199SXin Li    logging.info('Trying to reboot PID:%s', old_pid)
301*9c5db199SXin Li    adb_cmd('reboot', ignore_status=True)
302*9c5db199SXin Li    # Ensure that the old container is no longer booted
303*9c5db199SXin Li    utils.poll_for_condition(
304*9c5db199SXin Li        lambda: not utils.pid_is_alive(int(old_pid)), timeout=10)
305*9c5db199SXin Li
306*9c5db199SXin Li
307*9c5db199SXin Li# This adb_root() function is deceiving in that it works just fine on debug
308*9c5db199SXin Li# builds of ARC (user-debug, eng). However "adb root" does not work on user
309*9c5db199SXin Li# builds as run by the autotest machines when testing prerelease images. In fact
310*9c5db199SXin Li# it will silently fail. You will need to find another way to do do what you
311*9c5db199SXin Li# need to do as root.
312*9c5db199SXin Li#
313*9c5db199SXin Li# TODO(b/79122489) - Remove this function.
314*9c5db199SXin Lidef adb_root():
315*9c5db199SXin Li    """Restart adbd with root permission."""
316*9c5db199SXin Li
317*9c5db199SXin Li    adb_cmd('root')
318*9c5db199SXin Li
319*9c5db199SXin Li
320*9c5db199SXin Lidef get_container_root():
321*9c5db199SXin Li    """Returns path to Android container root directory."""
322*9c5db199SXin Li    return _ANDROID_CONTAINER_ROOT_PATH
323*9c5db199SXin Li
324*9c5db199SXin Li
325*9c5db199SXin Lidef get_container_pid_path():
326*9c5db199SXin Li    """Returns the container's PID file path.
327*9c5db199SXin Li
328*9c5db199SXin Li    Raises:
329*9c5db199SXin Li      TestError if no PID file is found, or more than one files are found.
330*9c5db199SXin Li    """
331*9c5db199SXin Li    # Find the PID file rather than the android-XXXXXX/ directory to ignore
332*9c5db199SXin Li    # stale and empty android-XXXXXX/ directories when they exist.
333*9c5db199SXin Li    arc_container_pid_files = glob.glob(_ANDROID_CONTAINER_PID_PATH)
334*9c5db199SXin Li
335*9c5db199SXin Li    if len(arc_container_pid_files) == 0:
336*9c5db199SXin Li        raise error.TestError('Android container.pid not available')
337*9c5db199SXin Li
338*9c5db199SXin Li    if len(arc_container_pid_files) > 1:
339*9c5db199SXin Li        raise error.TestError(
340*9c5db199SXin Li                'Multiple Android container.pid files found: %r. '
341*9c5db199SXin Li                'Reboot your DUT to recover.' % (arc_container_pid_files))
342*9c5db199SXin Li
343*9c5db199SXin Li    return arc_container_pid_files[0]
344*9c5db199SXin Li
345*9c5db199SXin Li
346*9c5db199SXin Lidef get_android_data_root():
347*9c5db199SXin Li    """Returns path to ChromeOS directory that bind-mounts Android's /data."""
348*9c5db199SXin Li    return _ANDROID_DATA_ROOT_PATH
349*9c5db199SXin Li
350*9c5db199SXin Li
351*9c5db199SXin Lidef get_container_pid():
352*9c5db199SXin Li    """Returns the PID of the container."""
353*9c5db199SXin Li    return utils.read_one_line(get_container_pid_path())
354*9c5db199SXin Li
355*9c5db199SXin Li
356*9c5db199SXin Lidef get_adbd_pid():
357*9c5db199SXin Li    """Returns the PID of the adbd proxy container."""
358*9c5db199SXin Li    if not os.path.exists(_ADBD_PID_PATH):
359*9c5db199SXin Li        # The adbd proxy does not run on all boards.
360*9c5db199SXin Li        return None
361*9c5db199SXin Li    return utils.read_one_line(_ADBD_PID_PATH)
362*9c5db199SXin Li
363*9c5db199SXin Li
364*9c5db199SXin Lidef is_android_process_running(process_name):
365*9c5db199SXin Li    """Return whether Android has completed booting.
366*9c5db199SXin Li
367*9c5db199SXin Li    @param process_name: Process name.
368*9c5db199SXin Li    """
369*9c5db199SXin Li    output = adb_shell('pgrep -c -f %s' % pipes.quote(process_name),
370*9c5db199SXin Li                       ignore_status=True)
371*9c5db199SXin Li    return int(output) > 0
372*9c5db199SXin Li
373*9c5db199SXin Li
374*9c5db199SXin Lidef check_android_file_exists(filename):
375*9c5db199SXin Li    """Checks whether the given file exists in the Android filesystem
376*9c5db199SXin Li
377*9c5db199SXin Li    @param filename: File to check.
378*9c5db199SXin Li    """
379*9c5db199SXin Li    return adb_shell(
380*9c5db199SXin Li        'test -e {} && echo FileExists'.format(pipes.quote(filename)),
381*9c5db199SXin Li        ignore_status=True).find("FileExists") >= 0
382*9c5db199SXin Li
383*9c5db199SXin Li
384*9c5db199SXin Lidef read_android_file(filename):
385*9c5db199SXin Li    """Reads a file in Android filesystem.
386*9c5db199SXin Li
387*9c5db199SXin Li    @param filename: File to read.
388*9c5db199SXin Li    """
389*9c5db199SXin Li    with tempfile.NamedTemporaryFile() as tmpfile:
390*9c5db199SXin Li        adb_cmd('pull %s %s' % (pipes.quote(filename),
391*9c5db199SXin Li                                pipes.quote(tmpfile.name)))
392*9c5db199SXin Li        with open(tmpfile.name) as f:
393*9c5db199SXin Li            return f.read()
394*9c5db199SXin Li
395*9c5db199SXin Li    return None
396*9c5db199SXin Li
397*9c5db199SXin Li
398*9c5db199SXin Lidef write_android_file(filename, data):
399*9c5db199SXin Li    """Writes to a file in Android filesystem.
400*9c5db199SXin Li
401*9c5db199SXin Li    @param filename: File to write.
402*9c5db199SXin Li    @param data: Data to write.
403*9c5db199SXin Li    """
404*9c5db199SXin Li    with tempfile.NamedTemporaryFile() as tmpfile:
405*9c5db199SXin Li        tmpfile.write(data)
406*9c5db199SXin Li        tmpfile.flush()
407*9c5db199SXin Li
408*9c5db199SXin Li        adb_cmd('push %s %s' % (pipes.quote(tmpfile.name),
409*9c5db199SXin Li                                pipes.quote(filename)))
410*9c5db199SXin Li
411*9c5db199SXin Li
412*9c5db199SXin Lidef _write_android_file(filename, data):
413*9c5db199SXin Li    """Writes to a file in Android filesystem.
414*9c5db199SXin Li
415*9c5db199SXin Li    This is an internal function used to bootstrap adb.
416*9c5db199SXin Li    Tests should use write_android_file instead.
417*9c5db199SXin Li    """
418*9c5db199SXin Li    android_cmd = 'cat > %s' % pipes.quote(filename)
419*9c5db199SXin Li    cros_cmd = 'android-sh -c %s' % pipes.quote(android_cmd)
420*9c5db199SXin Li    utils.run(cros_cmd, stdin=data)
421*9c5db199SXin Li
422*9c5db199SXin Li
423*9c5db199SXin Lidef get_android_file_stats(filename):
424*9c5db199SXin Li    """Returns an object of file stats for an Android file.
425*9c5db199SXin Li
426*9c5db199SXin Li    The returned object supported limited attributes, but can be easily extended
427*9c5db199SXin Li    if needed. Note that the value are all string.
428*9c5db199SXin Li
429*9c5db199SXin Li    This uses _android_shell to run as root, so that it can access to all files
430*9c5db199SXin Li    inside the container. On non-debuggable build, adb shell is not rootable.
431*9c5db199SXin Li    """
432*9c5db199SXin Li    mapping = {
433*9c5db199SXin Li        '%a': 'mode',
434*9c5db199SXin Li        '%g': 'gid',
435*9c5db199SXin Li        '%h': 'nlink',
436*9c5db199SXin Li        '%u': 'uid',
437*9c5db199SXin Li    }
438*9c5db199SXin Li    output = _android_shell(
439*9c5db199SXin Li        'stat -c "%s" %s' % (' '.join(mapping.keys()), pipes.quote(filename)),
440*9c5db199SXin Li        ignore_status=True)
441*9c5db199SXin Li    stats = output.split(' ')
442*9c5db199SXin Li    if len(stats) != len(mapping):
443*9c5db199SXin Li        raise error.TestError('Unexpected output from stat: %s' % output)
444*9c5db199SXin Li    _Stats = collections.namedtuple('_Stats', mapping.values())
445*9c5db199SXin Li    return _Stats(*stats)
446*9c5db199SXin Li
447*9c5db199SXin Li
448*9c5db199SXin Lidef remove_android_file(filename):
449*9c5db199SXin Li    """Removes a file in Android filesystem.
450*9c5db199SXin Li
451*9c5db199SXin Li    @param filename: File to remove.
452*9c5db199SXin Li    """
453*9c5db199SXin Li    adb_shell('rm -f %s' % pipes.quote(filename))
454*9c5db199SXin Li
455*9c5db199SXin Li
456*9c5db199SXin Lidef wait_for_android_boot(timeout=None):
457*9c5db199SXin Li    """Sleep until Android has completed booting or timeout occurs.
458*9c5db199SXin Li
459*9c5db199SXin Li    @param timeout: Timeout in seconds.
460*9c5db199SXin Li    """
461*9c5db199SXin Li    arc_common.wait_for_android_boot(timeout)
462*9c5db199SXin Li
463*9c5db199SXin Li
464*9c5db199SXin Lidef wait_for_android_process(process_name,
465*9c5db199SXin Li                             timeout=_WAIT_FOR_ANDROID_PROCESS_SECONDS):
466*9c5db199SXin Li    """Sleep until an Android process is running or timeout occurs.
467*9c5db199SXin Li
468*9c5db199SXin Li    @param process_name: Process name.
469*9c5db199SXin Li    @param timeout: Timeout in seconds.
470*9c5db199SXin Li    """
471*9c5db199SXin Li    condition = lambda: is_android_process_running(process_name)
472*9c5db199SXin Li    utils.poll_for_condition(condition=condition,
473*9c5db199SXin Li                             desc='%s is running' % process_name,
474*9c5db199SXin Li                             timeout=timeout,
475*9c5db199SXin Li                             sleep_interval=_PROCESS_CHECK_INTERVAL_SECONDS)
476*9c5db199SXin Li
477*9c5db199SXin Li
478*9c5db199SXin Lidef _android_shell(cmd, **kwargs):
479*9c5db199SXin Li    """Execute cmd instead the Android container.
480*9c5db199SXin Li
481*9c5db199SXin Li    This function is strictly for internal use only, as commands do not run in
482*9c5db199SXin Li    a fully consistent Android environment. Prefer adb_shell instead.
483*9c5db199SXin Li    """
484*9c5db199SXin Li    return utils.system_output('android-sh -c {}'.format(pipes.quote(cmd)),
485*9c5db199SXin Li                               **kwargs)
486*9c5db199SXin Li
487*9c5db199SXin Li
488*9c5db199SXin Lidef is_android_container_alive():
489*9c5db199SXin Li    """Check if android container is alive."""
490*9c5db199SXin Li    try:
491*9c5db199SXin Li        container_pid = get_container_pid()
492*9c5db199SXin Li    except Exception as e:
493*9c5db199SXin Li        logging.error('is_android_container_alive failed: %r', e)
494*9c5db199SXin Li        return False
495*9c5db199SXin Li    return utils.pid_is_alive(int(container_pid))
496*9c5db199SXin Li
497*9c5db199SXin Li
498*9c5db199SXin Lidef _is_in_installed_packages_list(package, option=None):
499*9c5db199SXin Li    """Check if a package is in the list returned by pm list packages.
500*9c5db199SXin Li
501*9c5db199SXin Li    adb must be ready.
502*9c5db199SXin Li
503*9c5db199SXin Li    @param package: Package in request.
504*9c5db199SXin Li    @param option: An option for the command adb shell pm list packages.
505*9c5db199SXin Li                   Valid values include '-s', '-3', '-d', and '-e'.
506*9c5db199SXin Li    """
507*9c5db199SXin Li    command = 'pm list packages'
508*9c5db199SXin Li    if option:
509*9c5db199SXin Li        command += ' ' + option
510*9c5db199SXin Li    packages = adb_shell(command).splitlines()
511*9c5db199SXin Li    package_entry = 'package:' + package
512*9c5db199SXin Li    ret = package_entry in packages
513*9c5db199SXin Li
514*9c5db199SXin Li    if not ret:
515*9c5db199SXin Li        logging.info('Could not find "%s" in %s',
516*9c5db199SXin Li                     package_entry, str(packages))
517*9c5db199SXin Li    return ret
518*9c5db199SXin Li
519*9c5db199SXin Li
520*9c5db199SXin Lidef is_package_installed(package):
521*9c5db199SXin Li    """Check if a package is installed. adb must be ready.
522*9c5db199SXin Li
523*9c5db199SXin Li    @param package: Package in request.
524*9c5db199SXin Li    """
525*9c5db199SXin Li    return _is_in_installed_packages_list(package)
526*9c5db199SXin Li
527*9c5db199SXin Li
528*9c5db199SXin Lidef is_package_disabled(package):
529*9c5db199SXin Li    """Check if an installed package is disabled. adb must be ready.
530*9c5db199SXin Li
531*9c5db199SXin Li    @param package: Package in request.
532*9c5db199SXin Li    """
533*9c5db199SXin Li    return _is_in_installed_packages_list(package, '-d')
534*9c5db199SXin Li
535*9c5db199SXin Li
536*9c5db199SXin Lidef get_package_install_path(package):
537*9c5db199SXin Li    """Returns the apk install location of the given package."""
538*9c5db199SXin Li    output = adb_shell('pm path {}'.format(pipes.quote(package)))
539*9c5db199SXin Li    return output.split(':')[1]
540*9c5db199SXin Li
541*9c5db199SXin Li
542*9c5db199SXin Lidef _before_iteration_hook(obj):
543*9c5db199SXin Li    """Executed by parent class before every iteration.
544*9c5db199SXin Li
545*9c5db199SXin Li    This function resets the run_once_finished flag before every iteration
546*9c5db199SXin Li    so we can detect failure on every single iteration.
547*9c5db199SXin Li
548*9c5db199SXin Li    Args:
549*9c5db199SXin Li        obj: the test itself
550*9c5db199SXin Li    """
551*9c5db199SXin Li    obj.run_once_finished = False
552*9c5db199SXin Li
553*9c5db199SXin Li
554*9c5db199SXin Lidef _after_iteration_hook(obj):
555*9c5db199SXin Li    """Executed by parent class after every iteration.
556*9c5db199SXin Li
557*9c5db199SXin Li    The parent class will handle exceptions and failures in the run and will
558*9c5db199SXin Li    always call this hook afterwards. Take a screenshot if the run has not
559*9c5db199SXin Li    been marked as finished (i.e. there was a failure/exception).
560*9c5db199SXin Li
561*9c5db199SXin Li    Args:
562*9c5db199SXin Li        obj: the test itself
563*9c5db199SXin Li    """
564*9c5db199SXin Li    if not obj.run_once_finished:
565*9c5db199SXin Li        if is_adb_connected():
566*9c5db199SXin Li            logging.debug('Recent activities dump:\n%s',
567*9c5db199SXin Li                          adb_shell('dumpsys activity recents',
568*9c5db199SXin Li                                    ignore_status=True))
569*9c5db199SXin Li        if not os.path.exists(_SCREENSHOT_DIR_PATH):
570*9c5db199SXin Li            os.mkdir(_SCREENSHOT_DIR_PATH, 0o755)
571*9c5db199SXin Li        obj.num_screenshots += 1
572*9c5db199SXin Li        if obj.num_screenshots <= _MAX_SCREENSHOT_NUM:
573*9c5db199SXin Li            logging.warning('Iteration %d failed, taking a screenshot.',
574*9c5db199SXin Li                            obj.iteration)
575*9c5db199SXin Li            try:
576*9c5db199SXin Li                utils.run('screenshot "{}/{}_iter{}.png"'.format(
577*9c5db199SXin Li                    _SCREENSHOT_DIR_PATH, _SCREENSHOT_BASENAME, obj.iteration))
578*9c5db199SXin Li            except Exception as e:
579*9c5db199SXin Li                logging.warning('Unable to capture screenshot. %s', e)
580*9c5db199SXin Li        else:
581*9c5db199SXin Li            logging.warning('Too many failures, no screenshot taken')
582*9c5db199SXin Li
583*9c5db199SXin Li
584*9c5db199SXin Lidef send_keycode(keycode):
585*9c5db199SXin Li    """Sends the given keycode to the container
586*9c5db199SXin Li
587*9c5db199SXin Li    @param keycode: keycode to send.
588*9c5db199SXin Li    """
589*9c5db199SXin Li    adb_shell('input keyevent {}'.format(keycode))
590*9c5db199SXin Li
591*9c5db199SXin Li
592*9c5db199SXin Lidef get_android_sdk_version():
593*9c5db199SXin Li    """Returns the Android SDK version.
594*9c5db199SXin Li
595*9c5db199SXin Li    This function can be called before Android container boots.
596*9c5db199SXin Li    """
597*9c5db199SXin Li    with open('/etc/lsb-release') as f:
598*9c5db199SXin Li        values = dict(line.split('=', 1) for line in f.read().splitlines())
599*9c5db199SXin Li    try:
600*9c5db199SXin Li        return int(values['CHROMEOS_ARC_ANDROID_SDK_VERSION'])
601*9c5db199SXin Li    except (KeyError, ValueError):
602*9c5db199SXin Li        raise error.TestError('Could not determine Android SDK version')
603*9c5db199SXin Li
604*9c5db199SXin Li
605*9c5db199SXin Lidef set_device_mode(device_mode, use_fake_sensor_with_lifetime_secs=0):
606*9c5db199SXin Li    """Sets the device in either Clamshell or Tablet mode.
607*9c5db199SXin Li
608*9c5db199SXin Li    "inject_powerd_input_event" might fail if the DUT does not support Tablet
609*9c5db199SXin Li    mode, and it will raise an |error.CmdError| exception. To prevent that, use
610*9c5db199SXin Li    the |use_fake_sensor_with_lifetime_secs| parameter.
611*9c5db199SXin Li
612*9c5db199SXin Li    @param device_mode: string with either 'clamshell' or 'tablet'
613*9c5db199SXin Li    @param use_fake_sensor_with_lifetime_secs: if > 0, it will create the
614*9c5db199SXin Li           input device with the given lifetime in seconds
615*9c5db199SXin Li    @raise ValueError: if passed invalid parameters
616*9c5db199SXin Li    @raise error.CmdError: if inject_powerd_input_event fails
617*9c5db199SXin Li    """
618*9c5db199SXin Li    valid_value = ('tablet', 'clamshell')
619*9c5db199SXin Li    if device_mode not in valid_value:
620*9c5db199SXin Li        raise ValueError('Invalid device_mode parameter: %s' % device_mode)
621*9c5db199SXin Li
622*9c5db199SXin Li    value = 1 if device_mode == 'tablet' else 0
623*9c5db199SXin Li
624*9c5db199SXin Li    args = ['--code=tablet', '--value=%d' % value]
625*9c5db199SXin Li
626*9c5db199SXin Li    if use_fake_sensor_with_lifetime_secs > 0:
627*9c5db199SXin Li        args.extend(['--create_dev', '--dev_lifetime=%d' %
628*9c5db199SXin Li                     use_fake_sensor_with_lifetime_secs])
629*9c5db199SXin Li
630*9c5db199SXin Li    try:
631*9c5db199SXin Li        utils.run('inject_powerd_input_event', args=args)
632*9c5db199SXin Li    except error.CmdError as err:
633*9c5db199SXin Li        # TODO: Fragile code ahead. Correct way to do it is to check
634*9c5db199SXin Li        # if device is already in desired mode, and do nothing if so.
635*9c5db199SXin Li        # ATM we don't have a way to check current device mode.
636*9c5db199SXin Li
637*9c5db199SXin Li        # Assuming that CmdError means that device does not support
638*9c5db199SXin Li        # --code=tablet parameter, meaning that device only supports clamshell
639*9c5db199SXin Li        # mode.
640*9c5db199SXin Li        if device_mode == 'clamshell' and \
641*9c5db199SXin Li                use_fake_sensor_with_lifetime_secs == 0:
642*9c5db199SXin Li            return
643*9c5db199SXin Li        raise err
644*9c5db199SXin Li
645*9c5db199SXin Li
646*9c5db199SXin Lidef wait_for_userspace_ready():
647*9c5db199SXin Li    """Waits for userspace apps to be launchable.
648*9c5db199SXin Li
649*9c5db199SXin Li    Launches and then closes Android settings as a way to ensure all basic
650*9c5db199SXin Li    services are ready. This goes a bit beyond waiting for boot-up to complete,
651*9c5db199SXin Li    as being able to launch an activity requires more of the framework to have
652*9c5db199SXin Li    started. The boot-complete signal happens fairly early, and the framework
653*9c5db199SXin Li    system server is still starting services. By waiting for ActivityManager to
654*9c5db199SXin Li    respond, we automatically wait on more services to be ready.
655*9c5db199SXin Li    """
656*9c5db199SXin Li    output = adb_shell('am start -W -a android.settings.SETTINGS',
657*9c5db199SXin Li                       ignore_status=True)
658*9c5db199SXin Li    if not output.endswith('Complete'):
659*9c5db199SXin Li        logging.debug('Output was: %s', output)
660*9c5db199SXin Li        raise error.TestError('Could not launch SETTINGS')
661*9c5db199SXin Li    adb_shell('am force-stop com.android.settings', ignore_status=True)
662*9c5db199SXin Li
663*9c5db199SXin Li
664*9c5db199SXin Liclass ArcTest(test.test):
665*9c5db199SXin Li    """ Base class of ARC Test.
666*9c5db199SXin Li
667*9c5db199SXin Li    This class could be used as super class of an ARC test for saving
668*9c5db199SXin Li    redundant codes for container bringup, autotest-dep package(s) including
669*9c5db199SXin Li    uiautomator setup if required, and apks install/remove during
670*9c5db199SXin Li    arc_setup/arc_teardown, respectively. By default arc_setup() is called in
671*9c5db199SXin Li    initialize() after Android have been brought up. It could also be
672*9c5db199SXin Li    overridden to perform non-default tasks. For example, a simple
673*9c5db199SXin Li    ArcHelloWorldTest can be just implemented with print 'HelloWorld' in its
674*9c5db199SXin Li    run_once() and no other functions are required. We could expect
675*9c5db199SXin Li    ArcHelloWorldTest would bring up browser and  wait for container up, then
676*9c5db199SXin Li    print 'Hello World', and shutdown browser after. As a precaution, if you
677*9c5db199SXin Li    overwrite initialize(), arc_setup(), or cleanup() function(s) in ARC test,
678*9c5db199SXin Li    remember to call the corresponding function(s) in this base class as well.
679*9c5db199SXin Li    """
680*9c5db199SXin Li    version = 1
681*9c5db199SXin Li    _PKG_UIAUTOMATOR = 'uiautomator'
682*9c5db199SXin Li    _FULL_PKG_NAME_UIAUTOMATOR = 'com.github.uiautomator'
683*9c5db199SXin Li
684*9c5db199SXin Li    def __init__(self, *args, **kwargs):
685*9c5db199SXin Li        """Initialize flag setting."""
686*9c5db199SXin Li        super(ArcTest, self).__init__(*args, **kwargs)
687*9c5db199SXin Li        self.initialized = False
688*9c5db199SXin Li        # Set the flag run_once_finished to detect if a test is executed
689*9c5db199SXin Li        # successfully without any exception thrown. Otherwise, generate
690*9c5db199SXin Li        # a screenshot in /var/log for debugging.
691*9c5db199SXin Li        self.run_once_finished = False
692*9c5db199SXin Li        self.logcat_proc = None
693*9c5db199SXin Li        self.dep_package = None
694*9c5db199SXin Li        self.apks = None
695*9c5db199SXin Li        self.full_pkg_names = []
696*9c5db199SXin Li        self.uiautomator = False
697*9c5db199SXin Li        self._should_reenable_play_store = False
698*9c5db199SXin Li        self._chrome = None
699*9c5db199SXin Li        if os.path.exists(_SCREENSHOT_DIR_PATH):
700*9c5db199SXin Li            shutil.rmtree(_SCREENSHOT_DIR_PATH)
701*9c5db199SXin Li        self.register_before_iteration_hook(_before_iteration_hook)
702*9c5db199SXin Li        self.register_after_iteration_hook(_after_iteration_hook)
703*9c5db199SXin Li        # Keep track of the number of debug screenshots taken and keep the
704*9c5db199SXin Li        # total number valid to avoid issues.
705*9c5db199SXin Li        self.num_screenshots = 0
706*9c5db199SXin Li
707*9c5db199SXin Li    def initialize(self, extension_path=None, username=None, password=None,
708*9c5db199SXin Li                   arc_mode=arc_common.ARC_MODE_ENABLED, **chrome_kargs):
709*9c5db199SXin Li        """Log in to a test account."""
710*9c5db199SXin Li        extension_paths = [extension_path] if extension_path else []
711*9c5db199SXin Li        self._chrome = chrome.Chrome(extension_paths=extension_paths,
712*9c5db199SXin Li                                     username=username,
713*9c5db199SXin Li                                     password=password,
714*9c5db199SXin Li                                     arc_mode=arc_mode,
715*9c5db199SXin Li                                     **chrome_kargs)
716*9c5db199SXin Li        if extension_path:
717*9c5db199SXin Li            self._extension = self._chrome.get_extension(extension_path)
718*9c5db199SXin Li        else:
719*9c5db199SXin Li            self._extension = None
720*9c5db199SXin Li        # With ARC enabled, Chrome will wait until container to boot up
721*9c5db199SXin Li        # before returning here, see chrome.py.
722*9c5db199SXin Li        self.initialized = True
723*9c5db199SXin Li        try:
724*9c5db199SXin Li            if is_android_container_alive():
725*9c5db199SXin Li                self.arc_setup()
726*9c5db199SXin Li            else:
727*9c5db199SXin Li                logging.error('Container is alive?')
728*9c5db199SXin Li        except Exception as err:
729*9c5db199SXin Li            raise error.TestFail(err)
730*9c5db199SXin Li
731*9c5db199SXin Li    def after_run_once(self):
732*9c5db199SXin Li        """Executed after run_once() only if there were no errors.
733*9c5db199SXin Li
734*9c5db199SXin Li        This function marks the run as finished with a flag. If there was a
735*9c5db199SXin Li        failure the flag won't be set and the failure can then be detected by
736*9c5db199SXin Li        testing the run_once_finished flag.
737*9c5db199SXin Li        """
738*9c5db199SXin Li        logging.info('After run_once')
739*9c5db199SXin Li        self.run_once_finished = True
740*9c5db199SXin Li
741*9c5db199SXin Li    def cleanup(self):
742*9c5db199SXin Li        """Log out of Chrome."""
743*9c5db199SXin Li        if not self.initialized:
744*9c5db199SXin Li            logging.info('Skipping ARC cleanup: not initialized')
745*9c5db199SXin Li            return
746*9c5db199SXin Li        logging.info('Starting ARC cleanup')
747*9c5db199SXin Li        try:
748*9c5db199SXin Li            if is_android_container_alive():
749*9c5db199SXin Li                self.arc_teardown()
750*9c5db199SXin Li        except Exception as err:
751*9c5db199SXin Li            raise error.TestFail(err)
752*9c5db199SXin Li        finally:
753*9c5db199SXin Li            try:
754*9c5db199SXin Li                if self.logcat_proc:
755*9c5db199SXin Li                    self.logcat_proc.close()
756*9c5db199SXin Li            finally:
757*9c5db199SXin Li                if self._chrome is not None:
758*9c5db199SXin Li                    self._chrome.close()
759*9c5db199SXin Li
760*9c5db199SXin Li    def _install_apks(self, dep_package, apks, full_pkg_names):
761*9c5db199SXin Li        """"Install apks fetched from the specified package folder.
762*9c5db199SXin Li
763*9c5db199SXin Li        @param dep_package: A dependent package directory
764*9c5db199SXin Li        @param apks: List of apk names to be installed
765*9c5db199SXin Li        @param full_pkg_names: List of packages to be uninstalled at teardown
766*9c5db199SXin Li        """
767*9c5db199SXin Li        apk_path = os.path.join(self.autodir, 'deps', dep_package)
768*9c5db199SXin Li        if apks:
769*9c5db199SXin Li            for apk in apks:
770*9c5db199SXin Li                logging.info('Installing %s', apk)
771*9c5db199SXin Li                out = adb_install('%s/%s' % (apk_path, apk), ignore_status=True)
772*9c5db199SXin Li                logging.info('Install apk output: %s', str(out))
773*9c5db199SXin Li            # Verify if package(s) are installed correctly.  We ignored
774*9c5db199SXin Li            # individual install statuses above because some tests list apks for
775*9c5db199SXin Li            # all arches and only need one installed.
776*9c5db199SXin Li            if not full_pkg_names:
777*9c5db199SXin Li                raise error.TestError('Package names of apks expected')
778*9c5db199SXin Li            for pkg in full_pkg_names:
779*9c5db199SXin Li                logging.info('Check if %s is installed', pkg)
780*9c5db199SXin Li                if not is_package_installed(pkg):
781*9c5db199SXin Li                    raise error.TestError('Package %s not found' % pkg)
782*9c5db199SXin Li                # Make sure full_pkg_names contains installed packages only
783*9c5db199SXin Li                # so arc_teardown() knows what packages to uninstall.
784*9c5db199SXin Li                self.full_pkg_names.append(pkg)
785*9c5db199SXin Li
786*9c5db199SXin Li    def _count_nested_array_level(self, array):
787*9c5db199SXin Li        """Count the level of a nested array."""
788*9c5db199SXin Li        if isinstance(array, list):
789*9c5db199SXin Li            return 1 + self._count_nested_array_level(array[0])
790*9c5db199SXin Li        return 0
791*9c5db199SXin Li
792*9c5db199SXin Li    def _fix_nested_array_level(self, var_name, expected_level, array):
793*9c5db199SXin Li        """Enclose array one level deeper if needed."""
794*9c5db199SXin Li        level = self._count_nested_array_level(array)
795*9c5db199SXin Li        if level == expected_level:
796*9c5db199SXin Li            return array
797*9c5db199SXin Li        if level == expected_level - 1:
798*9c5db199SXin Li            return [array]
799*9c5db199SXin Li
800*9c5db199SXin Li        logging.error("Variable %s nested level is not fixable: "
801*9c5db199SXin Li                      "Expecting %d, seeing %d",
802*9c5db199SXin Li                      var_name, expected_level, level)
803*9c5db199SXin Li        raise error.TestError('Format error with variable %s' % var_name)
804*9c5db199SXin Li
805*9c5db199SXin Li    def arc_setup(self, dep_packages=None, apks=None, full_pkg_names=None,
806*9c5db199SXin Li                  uiautomator=False, disable_play_store=False):
807*9c5db199SXin Li        """ARC test setup: Setup dependencies and install apks.
808*9c5db199SXin Li
809*9c5db199SXin Li        This function disables package verification and enables non-market
810*9c5db199SXin Li        APK installation. Then, it installs specified APK(s) and uiautomator
811*9c5db199SXin Li        package and path if required in a test.
812*9c5db199SXin Li
813*9c5db199SXin Li        @param dep_packages: Array of package names of autotest_deps APK
814*9c5db199SXin Li                             packages.
815*9c5db199SXin Li        @param apks: Array of APK name arrays to be installed in dep_package.
816*9c5db199SXin Li        @param full_pkg_names: Array of full package name arrays to be removed
817*9c5db199SXin Li                               in teardown.
818*9c5db199SXin Li        @param uiautomator: uiautomator python package is required or not.
819*9c5db199SXin Li        @param disable_play_store: Set this to True if you want to prevent
820*9c5db199SXin Li                                   GMS Core from updating.
821*9c5db199SXin Li        """
822*9c5db199SXin Li        if not self.initialized:
823*9c5db199SXin Li            logging.info('Skipping ARC setup: not initialized')
824*9c5db199SXin Li            return
825*9c5db199SXin Li        logging.info('Starting ARC setup')
826*9c5db199SXin Li
827*9c5db199SXin Li        # Sample parameters for multi-deps setup after fixup (if needed):
828*9c5db199SXin Li        # dep_packages: ['Dep1-apk', 'Dep2-apk']
829*9c5db199SXin Li        # apks: [['com.dep1.arch1.apk', 'com.dep2.arch2.apk'], ['com.dep2.apk']
830*9c5db199SXin Li        # full_pkg_nmes: [['com.dep1.app'], ['com.dep2.app']]
831*9c5db199SXin Li        # TODO(crbug/777787): once the parameters of all callers of arc_setup
832*9c5db199SXin Li        # are refactored, we can delete the safety net here.
833*9c5db199SXin Li        if dep_packages:
834*9c5db199SXin Li            dep_packages = self._fix_nested_array_level(
835*9c5db199SXin Li                'dep_packages', 1, dep_packages)
836*9c5db199SXin Li            apks = self._fix_nested_array_level('apks', 2, apks)
837*9c5db199SXin Li            full_pkg_names = self._fix_nested_array_level(
838*9c5db199SXin Li                'full_pkg_names', 2, full_pkg_names)
839*9c5db199SXin Li            if (len(dep_packages) != len(apks) or
840*9c5db199SXin Li                    len(apks) != len(full_pkg_names)):
841*9c5db199SXin Li                logging.info('dep_packages length is %d', len(dep_packages))
842*9c5db199SXin Li                logging.info('apks length is %d', len(apks))
843*9c5db199SXin Li                logging.info('full_pkg_names length is %d',
844*9c5db199SXin Li                             len(full_pkg_names))
845*9c5db199SXin Li                raise error.TestFail(
846*9c5db199SXin Li                    'dep_packages/apks/full_pkg_names format error')
847*9c5db199SXin Li
848*9c5db199SXin Li        self.dep_packages = dep_packages
849*9c5db199SXin Li        self.apks = apks
850*9c5db199SXin Li        self.uiautomator = uiautomator or disable_play_store
851*9c5db199SXin Li        # Setup dependent packages if required
852*9c5db199SXin Li        packages = []
853*9c5db199SXin Li        if dep_packages:
854*9c5db199SXin Li            packages = dep_packages[:]
855*9c5db199SXin Li        if self.uiautomator:
856*9c5db199SXin Li            packages.append(self._PKG_UIAUTOMATOR)
857*9c5db199SXin Li        if packages:
858*9c5db199SXin Li            logging.info('Setting up dependent package(s) %s', packages)
859*9c5db199SXin Li            self.job.setup_dep(packages)
860*9c5db199SXin Li
861*9c5db199SXin Li        self.logcat_proc = arc_common.Logcat()
862*9c5db199SXin Li
863*9c5db199SXin Li        wait_for_adb_ready()
864*9c5db199SXin Li
865*9c5db199SXin Li        # Setting verifier_verify_adb_installs to zero suppresses a dialog box
866*9c5db199SXin Li        # that can appear asking for the user to consent to the install.
867*9c5db199SXin Li        adb_shell('settings put global verifier_verify_adb_installs 0')
868*9c5db199SXin Li
869*9c5db199SXin Li        # Install apks based on dep_packages/apks/full_pkg_names tuples
870*9c5db199SXin Li        if dep_packages:
871*9c5db199SXin Li            for i in range(len(dep_packages)):
872*9c5db199SXin Li                self._install_apks(dep_packages[i], apks[i], full_pkg_names[i])
873*9c5db199SXin Li
874*9c5db199SXin Li        if self.uiautomator:
875*9c5db199SXin Li            path = os.path.join(self.autodir, 'deps', self._PKG_UIAUTOMATOR)
876*9c5db199SXin Li            sys.path.append(path)
877*9c5db199SXin Li            self._add_ui_object_not_found_handler()
878*9c5db199SXin Li        if disable_play_store and not is_package_disabled(_PLAY_STORE_PKG):
879*9c5db199SXin Li            self._disable_play_store()
880*9c5db199SXin Li            if not is_package_disabled(_PLAY_STORE_PKG):
881*9c5db199SXin Li                raise error.TestFail('Failed to disable Google Play Store.')
882*9c5db199SXin Li            self._should_reenable_play_store = True
883*9c5db199SXin Li
884*9c5db199SXin Li    def arc_teardown(self):
885*9c5db199SXin Li        """ARC test teardown.
886*9c5db199SXin Li
887*9c5db199SXin Li        This function removes all installed packages in arc_setup stage
888*9c5db199SXin Li        first. Then, it restores package verification and disables non-market
889*9c5db199SXin Li        APK installation.
890*9c5db199SXin Li
891*9c5db199SXin Li        """
892*9c5db199SXin Li        if self.full_pkg_names:
893*9c5db199SXin Li            for pkg in self.full_pkg_names:
894*9c5db199SXin Li                logging.info('Uninstalling %s', pkg)
895*9c5db199SXin Li                if not is_package_installed(pkg):
896*9c5db199SXin Li                    raise error.TestError('Package %s was not installed' % pkg)
897*9c5db199SXin Li                adb_uninstall(pkg)
898*9c5db199SXin Li        if (self.uiautomator and
899*9c5db199SXin Li            is_package_installed(self._FULL_PKG_NAME_UIAUTOMATOR)):
900*9c5db199SXin Li            logging.info('Uninstalling %s', self._FULL_PKG_NAME_UIAUTOMATOR)
901*9c5db199SXin Li            adb_uninstall(self._FULL_PKG_NAME_UIAUTOMATOR)
902*9c5db199SXin Li        if self._should_reenable_play_store:
903*9c5db199SXin Li            adb_shell('pm enable ' + _PLAY_STORE_PKG)
904*9c5db199SXin Li        adb_shell('settings put secure install_non_market_apps 0')
905*9c5db199SXin Li        adb_shell('settings put global package_verifier_enable 1')
906*9c5db199SXin Li        adb_shell('settings put secure package_verifier_user_consent 0')
907*9c5db199SXin Li
908*9c5db199SXin Li        # Remove the adb keys without going through adb. This is because the
909*9c5db199SXin Li        # 'rm' tool does not have permissions to remove the keys once they have
910*9c5db199SXin Li        # been restorecon(8)ed.
911*9c5db199SXin Li        utils.system_output('rm -f %s' %
912*9c5db199SXin Li                            pipes.quote(os.path.join(
913*9c5db199SXin Li                                get_android_data_root(),
914*9c5db199SXin Li                                os.path.relpath(_ANDROID_ADB_KEYS_PATH, '/'))))
915*9c5db199SXin Li        utils.system_output('adb kill-server')
916*9c5db199SXin Li
917*9c5db199SXin Li    def _add_ui_object_not_found_handler(self):
918*9c5db199SXin Li        """Logs the device dump upon uiautomator.UiObjectNotFoundException."""
919*9c5db199SXin Li        from uiautomator import device as d
920*9c5db199SXin Li        d.handlers.on(lambda d: logging.debug('Device window dump:\n%s',
921*9c5db199SXin Li                                              d.dump()))
922*9c5db199SXin Li
923*9c5db199SXin Li    def _disable_play_store(self):
924*9c5db199SXin Li        """Disables the Google Play Store app."""
925*9c5db199SXin Li        if is_package_disabled(_PLAY_STORE_PKG):
926*9c5db199SXin Li            return
927*9c5db199SXin Li        adb_shell('am force-stop ' + _PLAY_STORE_PKG)
928*9c5db199SXin Li        adb_shell('am start -a android.settings.APPLICATION_DETAILS_SETTINGS '
929*9c5db199SXin Li                  '-d package:' + _PLAY_STORE_PKG)
930*9c5db199SXin Li
931*9c5db199SXin Li        # Note: the straightforward "pm disable <package>" command would be
932*9c5db199SXin Li        # better, but that requires root permissions, which aren't available on
933*9c5db199SXin Li        # a pre-release image being tested. The only other way is through the
934*9c5db199SXin Li        # Settings UI, but which might change.
935*9c5db199SXin Li        from uiautomator import device as d
936*9c5db199SXin Li        d(textMatches='(?i)DISABLE', packageName=_SETTINGS_PKG).wait.exists()
937*9c5db199SXin Li        d(textMatches='(?i)DISABLE', packageName=_SETTINGS_PKG).click.wait()
938*9c5db199SXin Li        d(textMatches='(?i)DISABLE APP').click.wait()
939*9c5db199SXin Li        ok_button = d(textMatches='(?i)OK')
940*9c5db199SXin Li        if ok_button.exists:
941*9c5db199SXin Li            ok_button.click.wait()
942*9c5db199SXin Li        adb_shell('am force-stop ' + _SETTINGS_PKG)
943