1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import atexit
6import logging
7
8from telemetry.internal.platform.power_monitor import android_power_monitor_base
9
10def _ReenableChargingIfNeeded(battery):
11  if not battery.GetCharging():
12    battery.SetCharging(True)
13
14class AndroidPowerMonitorController(
15    android_power_monitor_base.AndroidPowerMonitorBase):
16  """
17  PowerMonitor that acts as facade for a list of PowerMonitor objects and uses
18  the first available one.
19  """
20  def __init__(self, power_monitors, battery):
21    super(AndroidPowerMonitorController, self).__init__()
22    self._candidate_power_monitors = power_monitors
23    self._active_monitors = []
24    self._battery = battery
25    atexit.register(_ReenableChargingIfNeeded, self._battery)
26
27  def CanMonitorPower(self):
28    return any(m.CanMonitorPower() for m in self._candidate_power_monitors)
29
30  def StartMonitoringPower(self, browser):
31    # TODO(rnephew): re-add assert when crbug.com/553601 is solved and
32    # StopMonitoringPower is called in the correct place.
33    if self._active_monitors:
34      logging.warning('StopMonitoringPower() not called when expected. Last '
35                      'results are likely not reported.')
36      self.StopMonitoringPower()
37    self._CheckStart()
38    self._ChargingOff(self._battery)
39    self._active_monitors = (
40        [m for m in self._candidate_power_monitors if m.CanMonitorPower()])
41    assert self._active_monitors, 'No available monitor.'
42    for monitor in self._active_monitors:
43      monitor.StartMonitoringPower(browser)
44
45  @staticmethod
46  def _MergePowerResults(combined_results, monitor_results):
47    """
48    Merges monitor_results into combined_results and leaves monitor_results
49    values if there are merge conflicts.
50    """
51    def _CheckDuplicateKeys(dict_one, dict_two, ignore_list=None):
52      for key in dict_one:
53        if key in dict_two and key not in ignore_list:
54          logging.warning('Found multiple instances of %s in power monitor '
55                          'entries. Using newest one.', key)
56    # Sub level power entries.
57    for part in ['platform_info', 'component_utilization']:
58      if part in monitor_results:
59        _CheckDuplicateKeys(combined_results[part], monitor_results[part])
60        combined_results[part].update(monitor_results[part])
61
62    # Top level power entries.
63    platform_info = combined_results['platform_info'].copy()
64    comp_utilization = combined_results['component_utilization'].copy()
65    _CheckDuplicateKeys(
66        combined_results, monitor_results,
67        ['identifier', 'platform_info', 'component_utilization'])
68    combined_results.update(monitor_results)
69    combined_results['platform_info'] = platform_info
70    combined_results['component_utilization'] = comp_utilization
71
72  def StopMonitoringPower(self):
73    self._CheckStop()
74    self._ChargingOn(self._battery)
75    try:
76      results = {'platform_info': {}, 'component_utilization': {}}
77      for monitor in self._active_monitors:
78        self._MergePowerResults(results, monitor.StopMonitoringPower())
79      return results
80    finally:
81      self._active_monitors = []
82
83  def _ChargingOff(self, battery):
84    battery.SetCharging(False)
85
86  def _ChargingOn(self, battery):
87    if battery.GetCharging():
88      logging.warning('Charging re-enabled during test.'
89                      'Results may be inaccurate.')
90    battery.SetCharging(True)
91