xref: /aosp_15_r20/external/autotest/client/cros/power/force_discharge_utils.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2021 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"""Helper class for power autotests that force DUT to discharge with EC."""
6
7from __future__ import absolute_import
8from __future__ import division
9from __future__ import print_function
10
11import logging
12import time
13
14from autotest_lib.client.common_lib import error
15from autotest_lib.client.cros import ec
16from autotest_lib.client.cros.power import power_utils
17from six.moves import range
18
19_FORCE_DISCHARGE_SETTINGS = ['false', 'true', 'optional']
20
21
22def _parse(force_discharge):
23    """
24    Parse and return force discharge setting.
25
26    @param force_discharge: string of whether to tell ec to discharge battery
27            even when the charger is plugged in. 'false' means no forcing
28            discharge; 'true' means forcing discharge and raising an error when
29            it fails; 'optional' means forcing discharge when possible but not
30            raising an error when it fails, which is more friendly to devices
31            without a battery.
32
33    @return: string representing valid force discharge setting.
34
35    @raise error.TestError: for invalid force discharge setting.
36
37    """
38    setting = str(force_discharge).lower()
39    if setting not in _FORCE_DISCHARGE_SETTINGS:
40        raise error.TestError(
41                'Force discharge setting \'%s\' need to be one of %s.' %
42                (str(force_discharge), _FORCE_DISCHARGE_SETTINGS))
43    return setting
44
45
46def _wait_for_battery_discharge(status):
47    """
48    Polling every 100ms for 2 seconds until battery is discharging. This
49    normally would take about 350ms.
50
51    @param status: DUT power status object.
52
53    @return: boolean indicating force discharge success.
54    """
55    for _ in range(20):
56        status.refresh()
57        if status.battery_discharging():
58            return True
59        time.sleep(0.1)
60    return False
61
62
63def process(force_discharge, status):
64    """
65    Perform force discharge steps.
66
67    @param force_discharge: string of whether to tell ec to discharge battery
68            even when the charger is plugged in. 'false' means no forcing
69            discharge; 'true' means forcing discharge and raising an error when
70            it fails; 'optional' means forcing discharge when possible but not
71            raising an error when it fails, which is more friendly to devices
72            without a battery.
73    @param status: DUT power status object.
74
75    @return: bool to indicate whether force discharge steps are successful. Note
76            that DUT cannot force discharge if DUT is not connected to AC.
77
78    @raise error.TestError: for invalid force discharge setting.
79    @raise error.TestNAError: when force_discharge is 'true' and the DUT is
80            incapable of forcing discharge.
81    @raise error.TestError: when force_discharge is 'true' and the DUT command
82            to force discharge fails.
83    """
84    force_discharge = _parse(force_discharge)
85
86    if force_discharge == 'true':
87        if not status.battery:
88            raise error.TestNAError('DUT does not have battery. '
89                                    'Could not force discharge.')
90        if not ec.has_cros_ec():
91            raise error.TestNAError('DUT does not have CrOS EC. '
92                                    'Could not force discharge.')
93        if not power_utils.charge_control_by_ectool(False):
94            raise error.TestError('Could not run battery force discharge.')
95        if not _wait_for_battery_discharge(status):
96            logging.warning('Battery does not report discharging state.')
97        return True
98    elif force_discharge == 'optional':
99        if not status.battery:
100            logging.warning('DUT does not have battery. '
101                            'Do not force discharge.')
102            return False
103        if not ec.has_cros_ec():
104            logging.warning('DUT does not have CrOS EC. '
105                            'Do not force discharge.')
106            return False
107        if not power_utils.charge_control_by_ectool(False):
108            logging.warning('Could not run battery force discharge. '
109                            'Do not force discharge.')
110            return False
111        if not _wait_for_battery_discharge(status):
112            logging.warning('Battery does not report discharging state.')
113        return True
114    elif force_discharge == 'false':
115        return False
116
117
118def restore(force_discharge_success):
119    """
120    Set DUT back to charging.
121
122    @param force_discharge_success: if DUT previously forced discharge
123            successfully, set DUT back to charging.
124    """
125    if force_discharge_success:
126        if not power_utils.charge_control_by_ectool(True):
127            logging.warning('Can not restore from force discharge.')
128