|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "content/browser/power_usage_monitor_impl.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/metrics/histogram.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/sys_info.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/browser/notification_service.h" | 
|  | #include "content/public/browser/notification_source.h" | 
|  | #include "content/public/browser/notification_types.h" | 
|  | #include "content/public/browser/power_usage_monitor.h" | 
|  | #include "content/public/browser/render_process_host.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Wait this long after power on before enabling power usage monitoring. | 
|  | const int kMinUptimeMinutes = 30; | 
|  |  | 
|  | // Minimum discharge time after which we collect the discharge rate. | 
|  | const int kMinDischargeMinutes = 30; | 
|  |  | 
|  | class PowerUsageMonitorSystemInterface | 
|  | : public PowerUsageMonitor::SystemInterface { | 
|  | public: | 
|  | explicit PowerUsageMonitorSystemInterface(PowerUsageMonitor* owner) | 
|  | : power_usage_monitor_(owner), | 
|  | weak_ptr_factory_(this) {} | 
|  | ~PowerUsageMonitorSystemInterface() override {} | 
|  |  | 
|  | void ScheduleHistogramReport(base::TimeDelta delay) override { | 
|  | BrowserThread::PostDelayedTask( | 
|  | BrowserThread::UI, | 
|  | FROM_HERE, | 
|  | base::Bind( | 
|  | &PowerUsageMonitorSystemInterface::ReportBatteryLevelHistogram, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | Now(), | 
|  | delay), | 
|  | delay); | 
|  | } | 
|  |  | 
|  | void CancelPendingHistogramReports() override { | 
|  | weak_ptr_factory_.InvalidateWeakPtrs(); | 
|  | } | 
|  |  | 
|  | void RecordDischargePercentPerHour(int percent_per_hour) override { | 
|  | UMA_HISTOGRAM_PERCENTAGE("Power.BatteryDischargePercentPerHour", | 
|  | percent_per_hour); | 
|  | } | 
|  |  | 
|  | base::Time Now() override { return base::Time::Now(); } | 
|  |  | 
|  | protected: | 
|  | void ReportBatteryLevelHistogram(base::Time start_time, | 
|  | base::TimeDelta discharge_time) { | 
|  | // It's conceivable that the code to cancel pending histogram reports on | 
|  | // system suspend, will only get called after the system has woken up. | 
|  | // To mitigage this, check whether more time has passed than expected and | 
|  | // abort histogram recording in this case. | 
|  |  | 
|  | // Delayed tasks are subject to timer coalescing and can fire anywhere from | 
|  | // delay -> delay * 1.5) . In most cases, the OS should fire the task | 
|  | // at the next wakeup and not as late as it can. | 
|  | // A threshold of 2 minutes is used, since that should be large enough to | 
|  | // take the slop factor due to coalescing into account. | 
|  | base::TimeDelta threshold = discharge_time + | 
|  | base::TimeDelta::FromMinutes(2); | 
|  | if ((Now() - start_time) > threshold) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const std::string histogram_name = base::StringPrintf( | 
|  | "Power.BatteryDischarge_%d", discharge_time.InMinutes()); | 
|  | base::HistogramBase* histogram = | 
|  | base::Histogram::FactoryGet(histogram_name, | 
|  | 1, | 
|  | 100, | 
|  | 101, | 
|  | base::Histogram::kUmaTargetedHistogramFlag); | 
|  | double discharge_amount = power_usage_monitor_->discharge_amount(); | 
|  | histogram->Add(discharge_amount * 100); | 
|  | } | 
|  |  | 
|  | private: | 
|  | PowerUsageMonitor* power_usage_monitor_;  // Not owned. | 
|  |  | 
|  | // Used to cancel in progress delayed tasks. | 
|  | base::WeakPtrFactory<PowerUsageMonitorSystemInterface> weak_ptr_factory_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void StartPowerUsageMonitor() { | 
|  | static base::LazyInstance<PowerUsageMonitor>::Leaky monitor = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  | monitor.Get().Start(); | 
|  | } | 
|  |  | 
|  | PowerUsageMonitor::PowerUsageMonitor() | 
|  | : callback_(base::Bind(&PowerUsageMonitor::OnBatteryStatusUpdate, | 
|  | base::Unretained(this))), | 
|  | system_interface_(new PowerUsageMonitorSystemInterface(this)), | 
|  | started_(false), | 
|  | tracking_discharge_(false), | 
|  | on_battery_power_(false), | 
|  | initial_battery_level_(0), | 
|  | current_battery_level_(0) { | 
|  | } | 
|  |  | 
|  | PowerUsageMonitor::~PowerUsageMonitor() { | 
|  | if (started_) | 
|  | base::PowerMonitor::Get()->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::Start() { | 
|  | // Power monitoring may be delayed based on uptime, but renderer process | 
|  | // lifetime tracking needs to start immediately so processes created before | 
|  | // then are accounted for. | 
|  | registrar_.Add(this, | 
|  | NOTIFICATION_RENDERER_PROCESS_CREATED, | 
|  | NotificationService::AllBrowserContextsAndSources()); | 
|  | registrar_.Add(this, | 
|  | NOTIFICATION_RENDERER_PROCESS_CLOSED, | 
|  | NotificationService::AllBrowserContextsAndSources()); | 
|  | subscription_ = | 
|  | device::BatteryStatusService::GetInstance()->AddCallback(callback_); | 
|  |  | 
|  | // Delay initialization until the system has been up for a while. | 
|  | // This is to mitigate the effect of increased power draw during system start. | 
|  | base::TimeDelta uptime = base::SysInfo::Uptime(); | 
|  | base::TimeDelta min_uptime = base::TimeDelta::FromMinutes(kMinUptimeMinutes); | 
|  | if (uptime < min_uptime) { | 
|  | base::TimeDelta delay = min_uptime - uptime; | 
|  | BrowserThread::PostDelayedTask( | 
|  | BrowserThread::UI, | 
|  | FROM_HERE, | 
|  | base::Bind(&PowerUsageMonitor::StartInternal, base::Unretained(this)), | 
|  | delay); | 
|  | } else { | 
|  | StartInternal(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::StartInternal() { | 
|  | DCHECK(!started_); | 
|  | started_ = true; | 
|  |  | 
|  | // PowerMonitor is used to get suspend/resume notifications. | 
|  | base::PowerMonitor::Get()->AddObserver(this); | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::DischargeStarted(double battery_level) { | 
|  | on_battery_power_ = true; | 
|  |  | 
|  | // If all browser windows are closed, don't report power metrics since | 
|  | // Chrome's power draw is likely not significant. | 
|  | if (live_renderer_ids_.empty()) | 
|  | return; | 
|  |  | 
|  | // Cancel any in-progress ReportBatteryLevelHistogram() calls. | 
|  | system_interface_->CancelPendingHistogramReports(); | 
|  |  | 
|  | tracking_discharge_ = true; | 
|  | start_discharge_time_ = system_interface_->Now(); | 
|  |  | 
|  | initial_battery_level_ = battery_level; | 
|  | current_battery_level_ = battery_level; | 
|  |  | 
|  | const int kBatteryReportingIntervalMinutes[] = {5, 15, 30}; | 
|  | for (auto reporting_interval : kBatteryReportingIntervalMinutes) { | 
|  | base::TimeDelta delay = base::TimeDelta::FromMinutes(reporting_interval); | 
|  | system_interface_->ScheduleHistogramReport(delay); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::WallPowerConnected(double battery_level) { | 
|  | on_battery_power_ = false; | 
|  |  | 
|  | if (tracking_discharge_) { | 
|  | DCHECK(!start_discharge_time_.is_null()); | 
|  | base::TimeDelta discharge_time = | 
|  | system_interface_->Now() - start_discharge_time_; | 
|  |  | 
|  | if (discharge_time.InMinutes() > kMinDischargeMinutes) { | 
|  | // Record the rate at which the battery discharged over the entire period | 
|  | // the system was on battery power. | 
|  | double discharge_hours = discharge_time.InSecondsF() / 3600.0; | 
|  | int percent_per_hour = | 
|  | floor(((discharge_amount() / discharge_hours) * 100.0) + 0.5); | 
|  | system_interface_->RecordDischargePercentPerHour(percent_per_hour); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Cancel any in-progress ReportBatteryLevelHistogram() calls. | 
|  | system_interface_->CancelPendingHistogramReports(); | 
|  |  | 
|  | initial_battery_level_ = 0; | 
|  | current_battery_level_ = 0; | 
|  | start_discharge_time_ = base::Time(); | 
|  | tracking_discharge_ = false; | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::OnBatteryStatusUpdate( | 
|  | const device::BatteryStatus& status) { | 
|  | bool now_on_battery_power = (status.charging == 0); | 
|  | bool was_on_battery_power = on_battery_power_; | 
|  | double battery_level = status.level; | 
|  |  | 
|  | if (now_on_battery_power == was_on_battery_power) { | 
|  | if (now_on_battery_power) | 
|  | current_battery_level_ = battery_level; | 
|  | return; | 
|  | } else if (now_on_battery_power) {  // Wall power disconnected. | 
|  | DischargeStarted(battery_level); | 
|  | } else {  // Wall power connected. | 
|  | WallPowerConnected(battery_level); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::OnRenderProcessNotification(int type, int rph_id) { | 
|  | size_t previous_num_live_renderers = live_renderer_ids_.size(); | 
|  |  | 
|  | if (type == NOTIFICATION_RENDERER_PROCESS_CREATED) { | 
|  | live_renderer_ids_.insert(rph_id); | 
|  | } else if (type == NOTIFICATION_RENDERER_PROCESS_CLOSED) { | 
|  | live_renderer_ids_.erase(rph_id); | 
|  | } else { | 
|  | NOTREACHED()  << "Unexpected notification type: " << type; | 
|  | } | 
|  |  | 
|  | if (live_renderer_ids_.empty() && previous_num_live_renderers != 0) { | 
|  | // All render processes have died. | 
|  | CancelPendingHistogramReporting(); | 
|  | tracking_discharge_ = false; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::SetSystemInterfaceForTest( | 
|  | scoped_ptr<SystemInterface> interface) { | 
|  | system_interface_ = std::move(interface); | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::OnPowerStateChange(bool on_battery_power) { | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::OnResume() { | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::OnSuspend() { | 
|  | CancelPendingHistogramReporting(); | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::Observe(int type, | 
|  | const NotificationSource& source, | 
|  | const NotificationDetails& details) { | 
|  | RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr(); | 
|  | OnRenderProcessNotification(type, rph->GetID()); | 
|  | } | 
|  |  | 
|  | void PowerUsageMonitor::CancelPendingHistogramReporting() { | 
|  | // Cancel any in-progress histogram reports and reporting of discharge UMA. | 
|  | system_interface_->CancelPendingHistogramReports(); | 
|  | } | 
|  |  | 
|  | }  // namespace content |