Add ProcessPowerCollector to audit CPU and power information.

This is part of the battery auditing feature, which will be surfaced in the Website Settings options page in the next patch.

BUG=372598

Review URL: https://codereview.chromium.org/472383002

Cr-Commit-Position: refs/heads/master@{#291999}
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 1aa16d90..8433194 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -48,6 +48,7 @@
   "+components/os_crypt",
   "+components/password_manager",
   "+components/policy",
+  "+components/power",
   "+components/precache",
   "+components/pref_registry",
   "+components/query_parser",
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 1c2ca9a..42206e6 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -69,6 +69,7 @@
 #include "chrome/browser/performance_monitor/performance_monitor.h"
 #include "chrome/browser/performance_monitor/startup_timer.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
+#include "chrome/browser/power/process_power_collector.h"
 #include "chrome/browser/pref_service_flags_storage.h"
 #include "chrome/browser/prefs/chrome_pref_service_factory.h"
 #include "chrome/browser/prefs/command_line_pref_store.h"
@@ -1551,6 +1552,11 @@
 
   performance_monitor::PerformanceMonitor::GetInstance()->Initialize();
 
+#if !defined(OS_ANDROID)
+  process_power_collector_.reset(new ProcessPowerCollector);
+  process_power_collector_->Initialize();
+#endif
+
   PostBrowserStart();
 
   if (parameters().ui_task) {
@@ -1625,6 +1631,10 @@
   // Disarm the startup hang detector time bomb if it is still Arm'ed.
   startup_watcher_->Disarm();
 
+  // Remove observers attached to D-Bus clients before DbusThreadManager is
+  // shut down.
+  process_power_collector_.reset();
+
   for (size_t i = 0; i < chrome_extra_parts_.size(); ++i)
     chrome_extra_parts_[i]->PostMainMessageLoopRun();
 
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index a856a2b7..21138a06 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -23,6 +23,7 @@
 class FieldTrialSynchronizer;
 class MetricsService;
 class PrefService;
+class ProcessPowerCollector;
 class Profile;
 class StartupBrowserCreator;
 class StartupTimeBomb;
@@ -150,6 +151,11 @@
 
   ChromeBrowserFieldTrials browser_field_trials_;
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+  // A monitor for attributing power consumption to origins.
+  scoped_ptr<ProcessPowerCollector> process_power_collector_;
+#endif
+
   // Vector of additional ChromeBrowserMainExtraParts.
   // Parts are deleted in the inverse order they are added.
   std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_;
diff --git a/chrome/browser/power/OWNERS b/chrome/browser/power/OWNERS
new file mode 100644
index 0000000..76a50a4
--- /dev/null
+++ b/chrome/browser/power/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/chrome/browser/power/process_power_collector.cc b/chrome/browser/power/process_power_collector.cc
new file mode 100644
index 0000000..71a16f8
--- /dev/null
+++ b/chrome/browser/power/process_power_collector.cc
@@ -0,0 +1,201 @@
+// 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 "chrome/browser/power/process_power_collector.h"
+
+#include "apps/app_window.h"
+#include "apps/app_window_registry.h"
+#include "base/process/process_handle.h"
+#include "base/process/process_metrics.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
+#include "components/power/origin_power_map.h"
+#include "components/power/origin_power_map_factory.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#endif
+
+namespace {
+const int kSecondsPerSample = 30;
+}
+
+ProcessPowerCollector::PerProcessData::PerProcessData(
+    scoped_ptr<base::ProcessMetrics> metrics,
+    const GURL& origin,
+    Profile* profile)
+    : metrics_(metrics.Pass()),
+      profile_(profile),
+      last_origin_(origin),
+      last_cpu_percent_(0),
+      seen_this_cycle_(true) {
+}
+
+ProcessPowerCollector::PerProcessData::PerProcessData()
+    : profile_(NULL),
+      last_cpu_percent_(0.0),
+      seen_this_cycle_(false) {
+}
+
+ProcessPowerCollector::PerProcessData::~PerProcessData() {
+}
+
+ProcessPowerCollector::ProcessPowerCollector()
+    : scale_factor_(1.0) {
+#if defined(OS_CHROMEOS)
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
+      this);
+#endif
+}
+
+ProcessPowerCollector::~ProcessPowerCollector() {
+#if defined(OS_CHROMEOS)
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
+      this);
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+void ProcessPowerCollector::PowerChanged(
+    const power_manager::PowerSupplyProperties& prop) {
+  if (prop.battery_state() ==
+      power_manager::PowerSupplyProperties::DISCHARGING) {
+    if (!timer_.IsRunning())
+      StartTimer();
+    scale_factor_ = prop.battery_discharge_rate();
+  } else {
+    timer_.Stop();
+  }
+}
+#endif
+
+void ProcessPowerCollector::Initialize() {
+  StartTimer();
+}
+
+double ProcessPowerCollector::UpdatePowerConsumptionForTesting() {
+  return UpdatePowerConsumption();
+}
+
+void ProcessPowerCollector::StartTimer() {
+  DCHECK(!timer_.IsRunning());
+  timer_.Start(FROM_HERE,
+               base::TimeDelta::FromSeconds(kSecondsPerSample),
+               this,
+               &ProcessPowerCollector::HandleUpdateTimeout);
+}
+
+double ProcessPowerCollector::UpdatePowerConsumption() {
+  double total_cpu_percent = SynchronizeProcesses();
+
+  for (ProcessMetricsMap::iterator it = metrics_map_.begin();
+       it != metrics_map_.end();
+       ++it) {
+    // Invalidate the process for the next cycle.
+    it->second->set_seen_this_cycle(false);
+  }
+
+  RecordCpuUsageByOrigin(total_cpu_percent);
+  return total_cpu_percent;
+}
+
+void ProcessPowerCollector::HandleUpdateTimeout() {
+  UpdatePowerConsumption();
+}
+
+double ProcessPowerCollector::SynchronizeProcesses() {
+  // Update all tabs.
+  for (TabContentsIterator it; !it.done(); it.Next()) {
+    content::RenderProcessHost* render_process = it->GetRenderProcessHost();
+    // Skip incognito web contents.
+    if (render_process->GetBrowserContext()->IsOffTheRecord())
+      continue;
+    UpdateProcessInMap(render_process, it->GetLastCommittedURL().GetOrigin());
+  }
+
+  // Iterate over all profiles to find all app windows to attribute all apps.
+  ProfileManager* pm = g_browser_process->profile_manager();
+  std::vector<Profile*> open_profiles = pm->GetLoadedProfiles();
+  for (std::vector<Profile*>::const_iterator it = open_profiles.begin();
+       it != open_profiles.end();
+       ++it) {
+    const apps::AppWindowRegistry::AppWindowList& app_windows =
+        apps::AppWindowRegistry::Get(*it)->app_windows();
+    for (apps::AppWindowRegistry::AppWindowList::const_iterator itr =
+             app_windows.begin();
+         itr != app_windows.end();
+         ++itr) {
+      content::WebContents* web_contents = (*itr)->web_contents();
+
+      UpdateProcessInMap(web_contents->GetRenderProcessHost(),
+                         web_contents->GetLastCommittedURL().GetOrigin());
+    }
+  }
+
+  // Remove invalid processes and sum up the cpu cycle.
+  double total_cpu_percent = 0.0;
+  ProcessMetricsMap::iterator it = metrics_map_.begin();
+  while (it != metrics_map_.end()) {
+    if (!it->second->seen_this_cycle()) {
+      metrics_map_.erase(it++);
+      continue;
+    }
+
+    total_cpu_percent += it->second->last_cpu_percent();
+    ++it;
+  }
+
+  return total_cpu_percent;
+}
+
+void ProcessPowerCollector::RecordCpuUsageByOrigin(double total_cpu_percent) {
+  DCHECK_GE(total_cpu_percent, 0);
+  if (total_cpu_percent == 0)
+    return;
+
+  for (ProcessMetricsMap::iterator it = metrics_map_.begin();
+       it != metrics_map_.end();
+       ++it) {
+    double last_process_power_usage = it->second->last_cpu_percent();
+    last_process_power_usage *= scale_factor_ / total_cpu_percent;
+
+    GURL origin = it->second->last_origin();
+    power::OriginPowerMap* origin_power_map =
+        power::OriginPowerMapFactory::GetForBrowserContext(
+            it->second->profile());
+    DCHECK(origin_power_map);
+    origin_power_map->AddPowerForOrigin(origin, last_process_power_usage);
+  }
+}
+
+void ProcessPowerCollector::UpdateProcessInMap(
+    const content::RenderProcessHost* rph,
+    const GURL& origin) {
+  base::ProcessHandle handle = rph->GetHandle();
+  if (metrics_map_.find(handle) == metrics_map_.end()) {
+    metrics_map_[handle] = linked_ptr<PerProcessData>(new PerProcessData(
+#if defined(OS_MACOSX)
+        scoped_ptr<base::ProcessMetrics>(
+            base::ProcessMetrics::CreateProcessMetrics(handle, NULL)),
+#else
+        scoped_ptr<base::ProcessMetrics>(
+            base::ProcessMetrics::CreateProcessMetrics(handle)),
+#endif
+        origin,
+        Profile::FromBrowserContext(rph->GetBrowserContext())));
+  }
+
+  linked_ptr<PerProcessData>& process_data = metrics_map_[handle];
+  process_data->set_last_cpu_percent(std::max(0.0,
+      cpu_usage_callback_.is_null() ? process_data->metrics()->GetCPUUsage()
+                                    : cpu_usage_callback_.Run(handle)));
+  process_data->set_seen_this_cycle(true);
+}
diff --git a/chrome/browser/power/process_power_collector.h b/chrome/browser/power/process_power_collector.h
new file mode 100644
index 0000000..b172a8f
--- /dev/null
+++ b/chrome/browser/power/process_power_collector.h
@@ -0,0 +1,146 @@
+// 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.
+
+#ifndef CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
+#define CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
+
+#include <map>
+
+#include "base/memory/linked_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/process/process_metrics.h"
+#include "base/timer/timer.h"
+#include "components/power/origin_power_map_factory.h"
+#include "url/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/power_manager_client.h"
+#endif
+
+class Profile;
+
+namespace content {
+class RenderProcessHost;
+}
+
+#if defined(OS_CHROMEOS)
+namespace power_manager {
+class PowerSupplyProperties;
+}
+#endif
+
+// Manages regular updates of the profile power consumption.
+class ProcessPowerCollector
+#if defined(OS_CHROMEOS)
+    : public chromeos::PowerManagerClient::Observer
+#endif
+      {
+ public:
+  class PerProcessData {
+   public:
+    PerProcessData(scoped_ptr<base::ProcessMetrics> metrics,
+                   const GURL& origin,
+                   Profile* profile);
+    PerProcessData();
+    ~PerProcessData();
+
+    base::ProcessMetrics* metrics() const { return metrics_.get(); }
+    Profile* profile() const { return profile_; }
+    GURL last_origin() const { return last_origin_; }
+    int last_cpu_percent() const { return last_cpu_percent_; }
+    bool seen_this_cycle() const { return seen_this_cycle_; }
+    void set_last_cpu_percent(double new_cpu) { last_cpu_percent_ = new_cpu; }
+    void set_seen_this_cycle(bool seen) { seen_this_cycle_ = seen; }
+
+   private:
+    // |metrics_| holds the ProcessMetrics information for the given process.
+    scoped_ptr<base::ProcessMetrics> metrics_;
+
+    // |profile| is the profile that is visiting the |last_origin_|.
+    // It is not owned by PerProcessData.
+    Profile* profile_;
+
+    // |last_origin_| is the last origin visited by the process.
+    GURL last_origin_;
+
+    // |last_cpu_percent_| is the proportion of the CPU used since the last
+    // query.
+    double last_cpu_percent_;
+
+    // |seen_this_cycle| represents if the process still exists in this cycle.
+    // If it doesn't, we erase the PerProcessData.
+    bool seen_this_cycle_;
+
+    DISALLOW_COPY_AND_ASSIGN(PerProcessData);
+  };
+
+  // A map from all process handles to a metric.
+  typedef std::map<base::ProcessHandle, linked_ptr<PerProcessData> >
+      ProcessMetricsMap;
+  // A callback used to define mock CPU usage for testing.
+  typedef base::Callback<double(base::ProcessHandle)> CpuUsageCallback;
+
+  // On Chrome OS, can only be initialized after the DBusThreadManager has been
+  // initialized.
+  ProcessPowerCollector();
+  // On Chrome OS, can only be destroyed before DBusThreadManager is.
+  virtual ~ProcessPowerCollector();
+
+  void set_cpu_usage_callback_for_testing(const CpuUsageCallback& callback) {
+    cpu_usage_callback_ = callback;
+  }
+
+  ProcessMetricsMap* metrics_map_for_testing() { return &metrics_map_; }
+
+#if defined(OS_CHROMEOS)
+  // PowerManagerClient::Observer implementation:
+  virtual void PowerChanged(
+      const power_manager::PowerSupplyProperties& prop) OVERRIDE;
+#endif
+
+  // Begin periodically updating the power consumption numbers by profile.
+  void Initialize();
+
+  // Calls UpdatePowerConsumption() and returns the total CPU percent.
+  double UpdatePowerConsumptionForTesting();
+
+ private:
+  // Starts the timer for updating the power consumption.
+  void StartTimer();
+
+  // Calls SynchronizerProcesses() and RecordCpuUsageByOrigin() to update the
+  // |metrics_map_| and attribute power consumption. Invoked by |timer_| and as
+  // a helper method for UpdatePowerConsumptionForTesting().
+  double UpdatePowerConsumption();
+
+  // Calls UpdatePowerConsumption(). Invoked by |timer_|.
+  void HandleUpdateTimeout();
+
+  // Synchronizes the currently active processes to the |metrics_map_| and
+  // returns the total amount of cpu usage in the cycle.
+  double SynchronizeProcesses();
+
+  // Attributes the power usage to the profiles and origins using the
+  // information from SynchronizeProcesses() given a total amount
+  // of CPU used in this cycle, |total_cpu_percent|.
+  void RecordCpuUsageByOrigin(double total_cpu_percent);
+
+  // Adds the information from a given RenderProcessHost to the |metrics_map_|
+  // for a given origin. Called by SynchronizeProcesses().
+  void UpdateProcessInMap(const content::RenderProcessHost* render_process,
+                          const GURL& origin);
+
+  ProcessMetricsMap metrics_map_;
+  base::RepeatingTimer<ProcessPowerCollector> timer_;
+
+  // Callback to use to get CPU usage if set.
+  CpuUsageCallback cpu_usage_callback_;
+
+  // The factor to scale the CPU usage by.
+  double scale_factor_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessPowerCollector);
+};
+
+#endif  // CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
diff --git a/chrome/browser/power/process_power_collector_unittest.cc b/chrome/browser/power/process_power_collector_unittest.cc
new file mode 100644
index 0000000..0a21e34
--- /dev/null
+++ b/chrome/browser/power/process_power_collector_unittest.cc
@@ -0,0 +1,314 @@
+// 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 "chrome/browser/power/process_power_collector.h"
+
+#include "apps/app_window_contents.h"
+#include "apps/app_window_registry.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/apps/chrome_app_delegate.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/power/origin_power_map.h"
+#include "components/power/origin_power_map_factory.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "extensions/browser/app_window/native_app_window.h"
+#include "extensions/common/extension.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#endif
+
+using power::OriginPowerMap;
+using power::OriginPowerMapFactory;
+
+class BrowserProcessPowerTest : public BrowserWithTestWindowTest {
+ public:
+  BrowserProcessPowerTest() {}
+  virtual ~BrowserProcessPowerTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    BrowserWithTestWindowTest::SetUp();
+    collector.reset(new ProcessPowerCollector);
+
+#if defined(OS_CHROMEOS)
+    power_manager::PowerSupplyProperties prop;
+    prop.set_external_power(power_manager::PowerSupplyProperties::AC);
+    prop.set_battery_state(power_manager::PowerSupplyProperties::DISCHARGING);
+    prop.set_battery_percent(20.00);
+    prop.set_battery_discharge_rate(1);
+    collector->PowerChanged(prop);
+#endif
+
+    profile_manager_.reset(
+        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+    ASSERT_TRUE(profile_manager_->SetUp());
+  }
+
+  virtual void TearDown() OVERRIDE {
+    collector.reset();
+    BrowserWithTestWindowTest::TearDown();
+  }
+
+  // Mocks out CPU usage for all processes as |value| percent.
+  double ReturnCpuAsConstant(double value, base::ProcessHandle handle) {
+    return value;
+  }
+
+ protected:
+  content::MockRenderProcessHost* GetProcess(Browser* browser) {
+    return static_cast<content::MockRenderProcessHost*>(
+        browser->tab_strip_model()
+            ->GetActiveWebContents()
+            ->GetRenderViewHost()
+            ->GetProcess());
+  }
+
+  scoped_ptr<base::ProcessHandle> MakeProcessHandle(int process_id) {
+    scoped_ptr<base::ProcessHandle> proc_handle(new base::ProcessHandle(
+#if defined(OS_WIN)
+        reinterpret_cast<HANDLE>(process_id))
+#else
+        process_id)
+#endif
+                                                );
+    return proc_handle.Pass();
+  }
+
+  scoped_ptr<ProcessPowerCollector> collector;
+  scoped_ptr<TestingProfileManager> profile_manager_;
+};
+
+class TestAppWindowContents : public apps::AppWindowContents {
+ public:
+  explicit TestAppWindowContents(content::WebContents* web_contents)
+      : web_contents_(web_contents) {}
+
+  // apps:AppWindowContents
+  virtual void Initialize(content::BrowserContext* context,
+                          const GURL& url) OVERRIDE {}
+  virtual void LoadContents(int32 creator_process_id) OVERRIDE {}
+  virtual void NativeWindowChanged(
+      extensions::NativeAppWindow* native_app_window) OVERRIDE {}
+  virtual void NativeWindowClosed() OVERRIDE {}
+  virtual void DispatchWindowShownForTests() const OVERRIDE {}
+  virtual content::WebContents* GetWebContents() const OVERRIDE {
+    return web_contents_.get();
+  }
+
+ private:
+  scoped_ptr<content::WebContents> web_contents_;
+};
+
+TEST_F(BrowserProcessPowerTest, NoSite) {
+  collector->UpdatePowerConsumptionForTesting();
+  EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
+}
+
+TEST_F(BrowserProcessPowerTest, OneSite) {
+  GURL url("http://www.google.com");
+  AddTab(browser(), url);
+  collector->UpdatePowerConsumptionForTesting();
+  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
+      collector->metrics_map_for_testing();
+  EXPECT_EQ(1u, metrics_map->size());
+
+  // Create fake process numbers.
+  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
+
+  OriginPowerMap* origin_power_map =
+      OriginPowerMapFactory::GetForBrowserContext(profile());
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
+
+  collector->set_cpu_usage_callback_for_testing(
+      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
+                 base::Unretained(this),
+                 5));
+  EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
+  EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
+}
+
+TEST_F(BrowserProcessPowerTest, MultipleSites) {
+  Browser::CreateParams native_params(profile(),
+                                      chrome::HOST_DESKTOP_TYPE_NATIVE);
+  GURL url1("http://www.google.com");
+  GURL url2("http://www.example.com");
+  GURL url3("https://www.google.com");
+  scoped_ptr<Browser> browser2(
+      chrome::CreateBrowserWithTestWindowForParams(&native_params));
+  scoped_ptr<Browser> browser3(
+      chrome::CreateBrowserWithTestWindowForParams(&native_params));
+  AddTab(browser(), url1);
+  AddTab(browser2.get(), url2);
+  AddTab(browser3.get(), url3);
+
+  // Create fake process numbers.
+  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
+  GetProcess(browser2.get())->SetProcessHandle(MakeProcessHandle(2).Pass());
+  GetProcess(browser3.get())->SetProcessHandle(MakeProcessHandle(3).Pass());
+
+  collector->UpdatePowerConsumptionForTesting();
+  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
+      collector->metrics_map_for_testing();
+  EXPECT_EQ(3u, metrics_map->size());
+
+  // Since all handlers are uninitialized, this should be 0.
+  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
+  OriginPowerMap* origin_power_map =
+      OriginPowerMapFactory::GetForBrowserContext(profile());
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url1));
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url2));
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url3));
+
+  collector->set_cpu_usage_callback_for_testing(
+      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
+                 base::Unretained(this),
+                 5));
+  EXPECT_DOUBLE_EQ(15, collector->UpdatePowerConsumptionForTesting());
+  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url1));
+  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url2));
+  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url3));
+
+  // Close some tabs and verify that they are removed from the metrics map.
+  chrome::CloseTab(browser2.get());
+  chrome::CloseTab(browser3.get());
+
+  collector->UpdatePowerConsumptionForTesting();
+  EXPECT_EQ(1u, metrics_map->size());
+}
+
+TEST_F(BrowserProcessPowerTest, IncognitoDoesntRecordPowerUsage) {
+  Browser::CreateParams native_params(profile()->GetOffTheRecordProfile(),
+                                      chrome::HOST_DESKTOP_TYPE_NATIVE);
+  scoped_ptr<Browser> incognito_browser(
+      chrome::CreateBrowserWithTestWindowForParams(&native_params));
+  GURL url("http://www.google.com");
+  AddTab(browser(), url);
+
+  GURL hidden_url("http://foo.com");
+  AddTab(incognito_browser.get(), hidden_url);
+
+  // Create fake process numbers.
+  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
+  GetProcess(incognito_browser.get())
+      ->SetProcessHandle(MakeProcessHandle(2).Pass());
+
+  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
+  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
+      collector->metrics_map_for_testing();
+  EXPECT_EQ(1u, metrics_map->size());
+
+  OriginPowerMap* origin_power_map =
+      OriginPowerMapFactory::GetForBrowserContext(profile());
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
+
+  collector->set_cpu_usage_callback_for_testing(
+      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
+                 base::Unretained(this),
+                 5));
+  EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
+
+  // Verify that the incognito data was not stored.
+  EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
+  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(hidden_url));
+
+  chrome::CloseTab(incognito_browser.get());
+}
+
+TEST_F(BrowserProcessPowerTest, MultipleProfilesRecordSeparately) {
+  scoped_ptr<Profile> other_profile(CreateProfile());
+  Browser::CreateParams native_params(other_profile.get(),
+                                      chrome::HOST_DESKTOP_TYPE_NATIVE);
+  scoped_ptr<Browser> other_user(
+      chrome::CreateBrowserWithTestWindowForParams(&native_params));
+
+  GURL url("http://www.google.com");
+  AddTab(browser(), url);
+
+  GURL hidden_url("http://foo.com");
+  AddTab(other_user.get(), hidden_url);
+
+  // Create fake process numbers.
+  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
+  GetProcess(other_user.get())->SetProcessHandle(MakeProcessHandle(2).Pass());
+
+  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
+  EXPECT_EQ(2u, collector->metrics_map_for_testing()->size());
+
+  collector->set_cpu_usage_callback_for_testing(
+      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
+                 base::Unretained(this),
+                 5));
+  EXPECT_DOUBLE_EQ(10, collector->UpdatePowerConsumptionForTesting());
+
+  // profile() should have an entry for |url| but not |hidden_url|.
+  OriginPowerMap* origin_power_map_first =
+      OriginPowerMapFactory::GetForBrowserContext(profile());
+  EXPECT_EQ(100, origin_power_map_first->GetPowerForOrigin(url));
+  EXPECT_EQ(0, origin_power_map_first->GetPowerForOrigin(hidden_url));
+
+  // |other_profile| should have an entry for |hidden_url| but not |url|.
+  OriginPowerMap* origin_power_map_second =
+      OriginPowerMapFactory::GetForBrowserContext(other_profile.get());
+  EXPECT_EQ(0, origin_power_map_second->GetPowerForOrigin(url));
+  EXPECT_EQ(100, origin_power_map_second->GetPowerForOrigin(hidden_url));
+
+  // Clean up
+  chrome::CloseTab(other_user.get());
+}
+
+TEST_F(BrowserProcessPowerTest, AppsRecordPowerUsage) {
+// Install an app (an extension*).
+#if defined(OS_WIN)
+  base::FilePath extension_path(FILE_PATH_LITERAL("c:\\foo"));
+#elif defined(OS_POSIX)
+  base::FilePath extension_path(FILE_PATH_LITERAL("/foo"));
+#endif
+  base::DictionaryValue manifest;
+  manifest.SetString("name", "Fake Name");
+  manifest.SetString("version", "1");
+  std::string error;
+  char kTestAppId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  scoped_refptr<extensions::Extension> extension(
+      extensions::Extension::Create(extension_path,
+                                    extensions::Manifest::INTERNAL,
+                                    manifest,
+                                    extensions::Extension::NO_FLAGS,
+                                    kTestAppId,
+                                    &error));
+  EXPECT_TRUE(extension.get()) << error;
+
+  Profile* current_profile =
+      profile_manager_->CreateTestingProfile("Test user");
+  GURL url("chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+  apps::AppWindow* window =
+      new apps::AppWindow(current_profile, new ChromeAppDelegate(), extension);
+  scoped_ptr<content::WebContents> web_contents(
+      content::WebContents::Create(content::WebContents::CreateParams(
+          current_profile,
+          content::SiteInstance::CreateForURL(current_profile, url))));
+  window->SetAppWindowContentsForTesting(scoped_ptr<apps::AppWindowContents>(
+      new TestAppWindowContents(web_contents.get())));
+  apps::AppWindowRegistry* app_registry =
+      apps::AppWindowRegistry::Get(current_profile);
+  app_registry->AddAppWindow(window);
+
+  collector->set_cpu_usage_callback_for_testing(
+      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
+                 base::Unretained(this),
+                 5));
+  collector->UpdatePowerConsumptionForTesting();
+  EXPECT_EQ(1u, collector->metrics_map_for_testing()->size());
+
+  app_registry->RemoveAppWindow(window);
+  collector->UpdatePowerConsumptionForTesting();
+  EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
+}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 1350d68e..ddb5b58d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -870,6 +870,8 @@
       'browser/platform_util_chromeos.cc',
       'browser/platform_util_mac.mm',
       'browser/platform_util_win.cc',
+      'browser/power/process_power_collector.cc',
+      'browser/power/process_power_collector.h',
       'browser/precache/most_visited_urls_provider.cc',
       'browser/precache/most_visited_urls_provider.h',
       'browser/predictors/autocomplete_action_predictor.cc',
@@ -2919,6 +2921,7 @@
             '../components/components.gyp:keyed_service_content',
             '../components/components.gyp:navigation_interception',
             '../components/components.gyp:password_manager_content_browser',
+            '../components/components.gyp:power',
             '../components/components.gyp:precache_content',
             '../components/components.gyp:sessions',
             '../components/components.gyp:storage_monitor',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 2af14f9..f8923bf 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1137,6 +1137,7 @@
         'browser/policy/policy_path_parser_unittest.cc',
         'browser/policy/profile_policy_connector_unittest.cc',
         'browser/policy/url_blacklist_manager_unittest.cc',
+        'browser/power/process_power_collector_unittest.cc',
         'browser/predictors/autocomplete_action_predictor_table_unittest.cc',
         'browser/predictors/autocomplete_action_predictor_unittest.cc',
         'browser/prefs/chrome_pref_service_unittest.cc',
@@ -2565,6 +2566,7 @@
             'browser/process_singleton_posix_unittest.cc',
             'browser/profiles/off_the_record_profile_impl_unittest.cc',
             'browser/profiles/profile_list_desktop_unittest.cc',
+            'browser/power/process_power_collector_unittest.cc',
             'browser/renderer_context_menu/render_view_context_menu_unittest.cc',
             'browser/search/instant_service_unittest.cc',
             'browser/search/search_unittest.cc',