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