1c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam# Use of this source code is governed by a BSD-style license that can be
3c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam# found in the LICENSE file.
4c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
5ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam"""A module to provide interface to OS services."""
6c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
76dc580a3b2ef48db27509a023fdfd64334aa946edanny chanimport datetime
8c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tamimport os
9c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tamimport re
10c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tamimport struct
1138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
1238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tamimport shell_wrapper
1338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
14c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
15ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tamclass OSInterfaceError(Exception):
16ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam    """OS interface specific exception."""
17c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    pass
18c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
19c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tamclass Crossystem(object):
20be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam    """A wrapper for the crossystem utility."""
21c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
22c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    # Code dedicated for user triggering recovery mode through crossystem.
23c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    USER_RECOVERY_REQUEST_CODE = '193'
24c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
25ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam    def init(self, os_if):
26be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Init the instance. If running on Mario - adjust the map."""
27ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam        self.os_if = os_if
28c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
29c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def __getattr__(self, name):
30be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
31c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Retrieve a crosssystem attribute.
32c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
33c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Attempt to access crossystemobject.name will invoke `crossystem name'
34c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        and return the stdout as the value.
35be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
36ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam        return self.os_if.run_shell_command_get_output(
37c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            'crossystem %s' % name)[0]
38c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
39c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def __setattr__(self, name, value):
40ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam        if name in ('os_if',):
41c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            self.__dict__[name] = value
42c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        else:
43ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam            self.os_if.run_shell_command('crossystem "%s=%s"' % (name, value))
44c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
45c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def request_recovery(self):
46be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Request recovery mode next time the target reboots."""
47c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
48c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.__setattr__('recovery_request', self.USER_RECOVERY_REQUEST_CODE)
49c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
50c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
51ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tamclass OSInterface(object):
52be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam    """An object to encapsulate OS services functions."""
53c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
54a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam    ANDROID_TESTER_FILE = '/mnt/stateful_partition/.android_faft_tester'
55a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam
56ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam    def __init__(self):
57ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam        """Object construction time initialization."""
58c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.state_dir = None
596dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        self.log_file = None
60c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.cs = Crossystem()
61a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        self.is_android = os.path.isfile(self.ANDROID_TESTER_FILE)
62a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        if self.is_android:
63a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            self.shell = shell_wrapper.AdbShell()
644a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            self.host_shell = shell_wrapper.LocalShell()
65a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        else:
66a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            self.shell = shell_wrapper.LocalShell()
674a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            self.host_shell = None
68a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam
69c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
706dc580a3b2ef48db27509a023fdfd64334aa946edanny chan    def init(self, state_dir=None, log_file=None):
71ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam        """Initialize the OS interface object.
7238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
73c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Args:
74c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam          state_dir - a string, the name of the directory (as defined by the
75c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                      caller). The contents of this directory persist over
76c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                      system restarts and power cycles.
776dc580a3b2ef48db27509a023fdfd64334aa946edanny chan          log_file - a string, the name of the log file kept in the state
786dc580a3b2ef48db27509a023fdfd64334aa946edanny chan                     directory.
79c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
80c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Default argument values support unit testing.
81be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
82c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.cs.init(self)
83c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.state_dir = state_dir
84c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
85c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if self.state_dir:
86c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            if not os.path.exists(self.state_dir):
87c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                try:
88c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                    os.mkdir(self.state_dir)
89c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                except OSError, err:
90ba5e3e52352ba60c43212f17c1d133006ef31ebdTom Wai-Hong Tam                    raise OSInterfaceError(err)
916dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            if log_file:
926dc580a3b2ef48db27509a023fdfd64334aa946edanny chan                if log_file[0] == '/':
936dc580a3b2ef48db27509a023fdfd64334aa946edanny chan                    self.log_file = log_file
946dc580a3b2ef48db27509a023fdfd64334aa946edanny chan                else:
956dc580a3b2ef48db27509a023fdfd64334aa946edanny chan                    self.log_file = os.path.join(state_dir, log_file)
96c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
9738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        # Initialize the shell. Should be after creating the log file.
9838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        self.shell.init(self)
994a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        if self.host_shell:
1004a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            self.host_shell.init(self)
10138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
102c7bdb7579baeeb7957001310cbd4dba63c4c43b3Tom Wai-Hong Tam    def has_host(self):
103c7bdb7579baeeb7957001310cbd4dba63c4c43b3Tom Wai-Hong Tam        """Return True if a host is connected to DUT."""
104c7bdb7579baeeb7957001310cbd4dba63c4c43b3Tom Wai-Hong Tam        return self.is_android
105c7bdb7579baeeb7957001310cbd4dba63c4c43b3Tom Wai-Hong Tam
10638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def run_shell_command(self, cmd):
10738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Run a shell command."""
10838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        self.shell.run_command(cmd)
10938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
11038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def run_shell_command_get_status(self, cmd):
11138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Run shell command and return its return code."""
11238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.shell.run_command_get_status(cmd)
11338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
11438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def run_shell_command_get_output(self, cmd):
11538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Run shell command and return its console output."""
11638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.shell.run_command_get_output(cmd)
11738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
11802da40da2f81fda288668ba663c5dee4db672ebbShelley Chen    def run_host_shell_command(self, cmd, block=True):
1194a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        """Run a shell command on the host."""
1204a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        if self.host_shell:
12102da40da2f81fda288668ba663c5dee4db672ebbShelley Chen            self.host_shell.run_command(cmd, block)
1224a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        else:
1234a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            raise OSInterfaceError('There is no host for DUT.')
1244a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam
1254a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam    def run_host_shell_command_get_status(self, cmd):
1264a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        """Run shell command and return its return code on the host."""
1274a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        if self.host_shell:
1284a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            return self.host_shell.run_command_get_status(cmd)
1294a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        else:
1304a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            raise OSInterfaceError('There is no host for DUT.')
1314a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam
1324a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam    def run_host_shell_command_get_output(self, cmd):
1334a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        """Run shell command and return its console output."""
1344a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        if self.host_shell:
1354a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            return self.host_shell.run_command_get_output(cmd)
1364a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam        else:
1374a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam            raise OSInterfaceError('There is no host for DUT.')
1384a1911eb368df8713a41c04adad8e704af01e06dTom Wai-Hong Tam
13938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def read_file(self, path):
14038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Read the content of the file."""
14138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.shell.read_file(path)
14238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
14338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def write_file(self, path, data):
14438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Write the data to the file."""
14538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        self.shell.write_file(path, data)
14638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
14738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def append_file(self, path, data):
14838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Append the data to the file."""
14938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        self.shell.append_file(path, data)
15038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
15138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def path_exists(self, path):
15238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Return True if the path exists on DUT."""
15338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'test -e %s' % path
15438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command_get_status(cmd) == 0
15538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
15638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def is_dir(self, path):
15738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Return True if the path is a directory."""
15838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'test -d %s' % path
15938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command_get_status(cmd) == 0
16038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
16138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def create_dir(self, path):
16238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Create a new directory."""
16338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'mkdir -p %s' % path
16438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command(cmd)
16538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
16638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def create_temp_file(self, prefix):
16738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Create a temporary file with a prefix."""
168a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        if self.is_android:
169a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            tmp_path = '/data/local/tmp'
170a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        else:
171a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            tmp_path = '/tmp'
172a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        cmd = 'mktemp -p %s %sXXXXXX' % (tmp_path, prefix)
17338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command_get_output(cmd)[0]
17438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
17538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def copy_file(self, from_path, to_path):
17638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Copy the file."""
17738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'cp -f %s %s' % (from_path, to_path)
17838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command(cmd)
17938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
18038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def copy_dir(self, from_path, to_path):
18138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Copy the directory."""
18238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'cp -rf %s %s' % (from_path, to_path)
18338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command(cmd)
18438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
18538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def remove_file(self, path):
18638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Remove the file."""
18738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'rm -f %s' % path
18838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command(cmd)
18938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
19038aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def remove_dir(self, path):
19138aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Remove the directory."""
19238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'rm -rf %s' % path
19338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return self.run_shell_command(cmd)
19438aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
19538aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam    def get_file_size(self, path):
19638aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        """Get the size of the file."""
19738aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        cmd = 'stat -c %%s %s' % path
19838aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        return int(self.run_shell_command_get_output(cmd)[0])
19938aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam
200c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def target_hosted(self):
201a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        """Return True if running on DUT."""
202a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        if self.is_android:
203a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            return True
204a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        signature = open('/etc/lsb-release', 'r').readlines()[0]
205c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return re.search(r'chrom(ium|e)os', signature, re.IGNORECASE) != None
206c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
207c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def state_dir_file(self, file_name):
208be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Get a full path of a file in the state directory."""
209c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return os.path.join(self.state_dir, file_name)
210c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
2113dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam    def wait_for_device(self, timeout):
2123dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam        """Wait for an Android device to be connected."""
2133dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam        return self.shell.wait_for_device(timeout)
2143dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam
2153dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam    def wait_for_no_device(self, timeout):
2163dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam        """Wait for no Android device to be connected (offline)."""
2173dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam        return self.shell.wait_for_no_device(timeout)
2183dbc70ac3c58465b4c35b5d09ab10d0e28abde7fTom Wai-Hong Tam
2196dc580a3b2ef48db27509a023fdfd64334aa946edanny chan    def log(self, text):
2206dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        """Write text to the log file and print it on the screen, if enabled.
2216dc580a3b2ef48db27509a023fdfd64334aa946edanny chan
2226dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        The entire log (maintained across reboots) can be found in
2236dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        self.log_file.
2246dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        """
2256dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        if not self.log_file or not os.path.exists(self.state_dir):
2266dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            # Called before environment was initialized, ignore.
2276dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            return
2286dc580a3b2ef48db27509a023fdfd64334aa946edanny chan
2296dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        timestamp = datetime.datetime.strftime(
2306dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            datetime.datetime.now(), '%I:%M:%S %p:')
2316dc580a3b2ef48db27509a023fdfd64334aa946edanny chan
2326dc580a3b2ef48db27509a023fdfd64334aa946edanny chan        with open(self.log_file, 'a') as log_f:
2336dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            log_f.write('%s %s\n' % (timestamp, text))
2346dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            log_f.flush()
2356dc580a3b2ef48db27509a023fdfd64334aa946edanny chan            os.fdatasync(log_f)
2366dc580a3b2ef48db27509a023fdfd64334aa946edanny chan
237c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def is_removable_device(self, device):
238be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Check if a certain storage device is removable.
239c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
240c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        device - a string, file name of a storage device or a device partition
241c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
242c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
243c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Returns True if the device is removable, False if not.
244be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
245a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        if self.is_android:
246a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            return False
247c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
248c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if not self.target_hosted():
249c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return False
250c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
251c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        # Drop trailing digit(s) and letter(s) (if any)
252c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        base_dev = self.strip_part(device.split('/')[2])
25338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        removable = int(self.read_file('/sys/block/%s/removable' % base_dev))
254c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
255c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return removable == 1
256c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
257c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def get_internal_disk(self, device):
258be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Get the internal disk by given the current disk.
259c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
260c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        If device is removable device, internal disk is decided by which kind
261c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        of divice (arm or x86). Otherwise, return device itself.
262c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
263c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        device - a string, file name of a storage device or a device partition
264c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
265c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
266c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Return internal kernel disk.
267be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
268c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if self.is_removable_device(device):
269bb61db3fde73d93429fe30e032d86f5178f97bcfDuncan Laurie            for p in ('/dev/mmcblk0', '/dev/mmcblk1', '/dev/nvme0n1'):
2707bc9bf3d2e63f3e4bbf9bfeca64728f7c36e69c5Vadim Bendebury                if self.path_exists(p):
2717bc9bf3d2e63f3e4bbf9bfeca64728f7c36e69c5Vadim Bendebury                    return p
2727bc9bf3d2e63f3e4bbf9bfeca64728f7c36e69c5Vadim Bendebury            return '/dev/sda'
273c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        else:
274c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return self.strip_part(device)
275c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
276c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def get_root_part(self):
277be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Return a string, the name of root device with partition number"""
278a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        # FIXME(waihong): Android doesn't support dual kernel/root and misses
279a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        # the related tools. Just return something that not break the existing
280a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        # code.
281a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        if self.is_android:
282a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            return '/dev/mmcblk0p3'
283a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam        else:
284a0265e39f0133d5e7699a4a675891cf71074aa95Tom Wai-Hong Tam            return self.run_shell_command_get_output('rootdev -s')[0]
285c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
286c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def get_root_dev(self):
287be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Return a string, the name of root device without partition number"""
288c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return self.strip_part(self.get_root_part())
289c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
290c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def join_part(self, dev, part):
291be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Return a concatenated string of device and partition number"""
292427c7b23118bf30175ef14327459a4db0adc381cGwendal Grignou        if dev.endswith(tuple(str(i) for i in range(0, 10))):
29357159bdb256fc9a68f940e368613967516e44d6eDuncan Laurie            return dev + 'p' + part
294c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        else:
295c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return dev + part
296c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
297c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def strip_part(self, dev_with_part):
298be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Return a stripped string without partition number"""
299c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        dev_name_stripper = re.compile('p?[0-9]+$')
300c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return dev_name_stripper.sub('', dev_with_part)
301c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
302c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def retrieve_body_version(self, blob):
303be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Given a blob, retrieve body version.
304c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
305c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Currently works for both, firmware and kernel blobs. Returns '-1' in
306c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        case the version can not be retrieved reliably.
307be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
308c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        header_format = '<8s8sQ'
309c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        preamble_format = '<40sQ'
310c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        magic, _, kb_size = struct.unpack_from(header_format, blob)
311c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
312c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if magic != 'CHROMEOS':
313c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return -1  # This could be a corrupted version case.
314c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
315c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        _, version = struct.unpack_from(preamble_format, blob, kb_size)
316c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return version
317c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
318c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def retrieve_datakey_version(self, blob):
319be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Given a blob, retrieve firmware data key version.
320c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
321c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        Currently works for both, firmware and kernel blobs. Returns '-1' in
322c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        case the version can not be retrieved reliably.
323be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
324c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        header_format = '<8s96sQ'
325c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        magic, _, version = struct.unpack_from(header_format, blob)
326c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if magic != 'CHROMEOS':
327c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return -1 # This could be a corrupted version case.
328c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return version
329c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
330c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def retrieve_kernel_subkey_version(self, blob):
331be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Given a blob, retrieve kernel subkey version.
332c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
333c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        It is in firmware vblock's preamble.
334be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
335c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
336c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        header_format = '<8s8sQ'
337c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        preamble_format = '<72sQ'
338c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        magic, _, kb_size = struct.unpack_from(header_format, blob)
339c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
340c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if magic != 'CHROMEOS':
341c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return -1
342c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
343c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        _, version = struct.unpack_from(preamble_format, blob, kb_size)
344c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return version
345c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
346c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def retrieve_preamble_flags(self, blob):
347be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Given a blob, retrieve preamble flags if available.
348c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
349c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        It only works for firmware. If the version of preamble header is less
350c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        than 2.1, no preamble flags supported, just returns 0.
351be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """
352c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        header_format = '<8s8sQ'
353c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        preamble_format = '<32sII64sI'
354c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        magic, _, kb_size = struct.unpack_from(header_format, blob)
355c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
356c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if magic != 'CHROMEOS':
357c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return -1  # This could be a corrupted version case.
358c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
359c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        _, ver, subver, _, flags = struct.unpack_from(preamble_format, blob,
360c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                                                      kb_size)
361c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
362c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        if ver > 2 or (ver == 2 and subver >= 1):
363c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return flags
364c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        else:
365c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam            return 0  # Returns 0 if preamble flags not available.
366c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam
367c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam    def read_partition(self, partition, size):
368be15328b1bbcf94c43e3435bbb4777a5dab259bbTom Wai-Hong Tam        """Read the requested partition, up to size bytes."""
369c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        tmp_file = self.state_dir_file('part.tmp')
370c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        self.run_shell_command('dd if=%s of=%s bs=1 count=%d' % (
371c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam                partition, tmp_file, size))
37238aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        data = self.read_file(tmp_file)
37338aa548ceb0f1fdb081deb9389b4c8e99aee485bTom Wai-Hong Tam        self.remove_file(tmp_file)
374c0168917ffb61c59c7746cd39fc53c1dd134723bTom Wai-Hong Tam        return data
375