summaryrefslogtreecommitdiff
path: root/chromium/base/power_monitor
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/power_monitor')
-rw-r--r--chromium/base/power_monitor/power_monitor.cc28
-rw-r--r--chromium/base/power_monitor/power_monitor.h14
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source.h9
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source_mac.mm13
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source_unittest.cc39
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source_win.cc1
-rw-r--r--chromium/base/power_monitor/power_monitor_source.cc32
-rw-r--r--chromium/base/power_monitor/power_monitor_source.h12
-rw-r--r--chromium/base/power_monitor/power_monitor_unittest.cc60
-rw-r--r--chromium/base/power_monitor/power_observer.h24
-rw-r--r--chromium/base/power_monitor/thermal_state_observer_mac.h40
-rw-r--r--chromium/base/power_monitor/thermal_state_observer_mac.mm75
-rw-r--r--chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm52
13 files changed, 381 insertions, 18 deletions
diff --git a/chromium/base/power_monitor/power_monitor.cc b/chromium/base/power_monitor/power_monitor.cc
index 0a48f30f521..18cfacf17fa 100644
--- a/chromium/base/power_monitor/power_monitor.cc
+++ b/chromium/base/power_monitor/power_monitor.cc
@@ -7,8 +7,9 @@
#include <atomic>
#include <utility>
+#include "base/logging.h"
#include "base/power_monitor/power_monitor_source.h"
-#include "base/trace_event/trace_event.h"
+#include "base/trace_event/base_tracing.h"
namespace base {
@@ -25,12 +26,8 @@ bool PowerMonitor::IsInitialized() {
return GetInstance()->source_.get() != nullptr;
}
-bool PowerMonitor::AddObserver(PowerObserver* obs) {
- PowerMonitor* power_monitor = GetInstance();
- if (!IsInitialized())
- return false;
- power_monitor->observers_->AddObserver(obs);
- return true;
+void PowerMonitor::AddObserver(PowerObserver* obs) {
+ GetInstance()->observers_->AddObserver(obs);
}
void PowerMonitor::RemoveObserver(PowerObserver* obs) {
@@ -47,15 +44,19 @@ bool PowerMonitor::IsOnBatteryPower() {
}
void PowerMonitor::ShutdownForTesting() {
- PowerMonitor::GetInstance()->observers_->AssertEmpty();
GetInstance()->source_ = nullptr;
- g_is_process_suspended.store(false);
+ g_is_process_suspended.store(false, std::memory_order_relaxed);
}
bool PowerMonitor::IsProcessSuspended() {
return g_is_process_suspended.load(std::memory_order_relaxed);
}
+PowerObserver::DeviceThermalState PowerMonitor::GetCurrentThermalState() {
+ DCHECK(IsInitialized());
+ return GetInstance()->source_->GetCurrentThermalState();
+}
+
void PowerMonitor::NotifyPowerStateChange(bool battery_in_use) {
DCHECK(IsInitialized());
DVLOG(1) << "PowerStateChange: " << (battery_in_use ? "On" : "Off")
@@ -82,6 +83,15 @@ void PowerMonitor::NotifyResume() {
GetInstance()->observers_->Notify(FROM_HERE, &PowerObserver::OnResume);
}
+void PowerMonitor::NotifyThermalStateChange(
+ PowerObserver::DeviceThermalState new_state) {
+ DCHECK(IsInitialized());
+ DVLOG(1) << "ThermalStateChange: "
+ << PowerMonitorSource::DeviceThermalStateToString(new_state);
+ GetInstance()->observers_->Notify(
+ FROM_HERE, &PowerObserver::OnThermalStateChange, new_state);
+}
+
PowerMonitor* PowerMonitor::GetInstance() {
static base::NoDestructor<PowerMonitor> power_monitor;
return power_monitor.get();
diff --git a/chromium/base/power_monitor/power_monitor.h b/chromium/base/power_monitor/power_monitor.h
index fcf5ee482a5..6b80f3d2dd4 100644
--- a/chromium/base/power_monitor/power_monitor.h
+++ b/chromium/base/power_monitor/power_monitor.h
@@ -40,12 +40,10 @@ class BASE_EXPORT PowerMonitor {
// from which it was registered.
// Must not be called from within a notification callback.
//
- // AddObserver() fails and returns false if PowerMonitor::Initialize() has not
- // been invoked. Failure should only happen in unit tests, where the
- // PowerMonitor is generally not initialized. It is safe to call
- // RemoveObserver with a PowerObserver that was not successfully added as an
+ // It is safe to add observers before the PowerMonitor is initialized. It is
+ // safe to call RemoveObserver with a PowerObserver that was not added as an
// observer.
- static bool AddObserver(PowerObserver* observer);
+ static void AddObserver(PowerObserver* observer);
static void RemoveObserver(PowerObserver* observer);
// Is the computer currently on battery power. May only be called if the
@@ -58,6 +56,10 @@ class BASE_EXPORT PowerMonitor {
// what is the real power state.
static bool IsProcessSuspended();
+ // Read the current DeviceThermalState if known. Can be called on any thread.
+ // May only be called if the PowerMonitor has been initialized.
+ static PowerObserver::DeviceThermalState GetCurrentThermalState();
+
// Uninitializes the PowerMonitor. Should be called at the end of any unit
// test that mocks out the PowerMonitor, to avoid affecting subsequent tests.
// There must be no live PowerObservers when invoked. Safe to call even if the
@@ -76,6 +78,8 @@ class BASE_EXPORT PowerMonitor {
static void NotifyPowerStateChange(bool battery_in_use);
static void NotifySuspend();
static void NotifyResume();
+ static void NotifyThermalStateChange(
+ PowerObserver::DeviceThermalState new_state);
static PowerMonitor* GetInstance();
diff --git a/chromium/base/power_monitor/power_monitor_device_source.h b/chromium/base/power_monitor/power_monitor_device_source.h
index 409cf0902f6..83b057f139a 100644
--- a/chromium/base/power_monitor/power_monitor_device_source.h
+++ b/chromium/base/power_monitor/power_monitor_device_source.h
@@ -20,6 +20,7 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_ionotificationportref.h"
+#include "base/power_monitor/thermal_state_observer_mac.h"
#endif
#if defined(OS_IOS)
@@ -46,6 +47,8 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource {
#endif
private:
+ friend class PowerMonitorDeviceSourceTest;
+
#if defined(OS_WIN)
// Represents a message-only window for power message handling on Windows.
// Only allow PowerMonitor to create it.
@@ -85,6 +88,9 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource {
bool IsOnBatteryPowerImpl() override;
#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // PowerMonitorSource:
+ PowerObserver::DeviceThermalState GetCurrentThermalState() override;
+
// Reference to the system IOPMrootDomain port.
io_connect_t power_manager_port_ = IO_OBJECT_NULL;
@@ -96,6 +102,9 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource {
// Run loop source to observe power-source-change events.
ScopedCFTypeRef<CFRunLoopSourceRef> power_source_run_loop_source_;
+
+ // Observer of thermal state events: critical temperature etc.
+ std::unique_ptr<ThermalStateObserverMac> thermal_state_observer_;
#endif
#if defined(OS_IOS)
diff --git a/chromium/base/power_monitor/power_monitor_device_source_mac.mm b/chromium/base/power_monitor/power_monitor_device_source_mac.mm
index 71c7403e00c..8618be1549c 100644
--- a/chromium/base/power_monitor/power_monitor_device_source_mac.mm
+++ b/chromium/base/power_monitor/power_monitor_device_source_mac.mm
@@ -51,6 +51,14 @@ bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
return true;
}
+PowerObserver::DeviceThermalState
+PowerMonitorDeviceSource::GetCurrentThermalState() {
+ if (@available(macOS 10.10.3, *)) {
+ return thermal_state_observer_->GetCurrentThermalState();
+ };
+ return PowerObserver::DeviceThermalState::kUnknown;
+}
+
namespace {
void BatteryEventCallback(void*) {
@@ -82,6 +90,11 @@ void PowerMonitorDeviceSource::PlatformInit() {
CFRunLoopAddSource(CFRunLoopGetCurrent(), power_source_run_loop_source_,
kCFRunLoopDefaultMode);
+
+ if (@available(macOS 10.10.3, *)) {
+ thermal_state_observer_ = std::make_unique<ThermalStateObserverMac>(
+ BindRepeating(&PowerMonitorSource::ProcessThermalEvent));
+ };
}
void PowerMonitorDeviceSource::PlatformDestroy() {
diff --git a/chromium/base/power_monitor/power_monitor_device_source_unittest.cc b/chromium/base/power_monitor/power_monitor_device_source_unittest.cc
new file mode 100644
index 00000000000..889f7577ade
--- /dev/null
+++ b/chromium/base/power_monitor/power_monitor_device_source_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 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 "base/power_monitor/power_monitor_device_source.h"
+
+#include "base/logging.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using DeviceThermalState = base::PowerObserver::DeviceThermalState;
+
+namespace base {
+
+class PowerMonitorDeviceSourceTest : public testing::Test {
+ public:
+ PowerMonitorDeviceSourceTest() = default;
+ ~PowerMonitorDeviceSourceTest() override = default;
+
+ DeviceThermalState GetCurrentThermalState() {
+ return power_monitor_device_source_.GetCurrentThermalState();
+ }
+
+ PowerMonitorDeviceSource power_monitor_device_source_;
+};
+
+TEST_F(PowerMonitorDeviceSourceTest, GetCurrentThermalState) {
+ const DeviceThermalState current_state = GetCurrentThermalState();
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // We cannot make assumptions on |current_state|. Print it out to use the var.
+ DVLOG(1) << PowerMonitorSource::DeviceThermalStateToString(current_state);
+#else
+ EXPECT_EQ(current_state, DeviceThermalState::kUnknown);
+#endif
+}
+
+} // namespace base
diff --git a/chromium/base/power_monitor/power_monitor_device_source_win.cc b/chromium/base/power_monitor/power_monitor_device_source_win.cc
index f7351356307..ae5ded7ea06 100644
--- a/chromium/base/power_monitor/power_monitor_device_source_win.cc
+++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc
@@ -4,6 +4,7 @@
#include "base/power_monitor/power_monitor_device_source.h"
+#include "base/logging.h"
#include "base/message_loop/message_loop_current.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
diff --git a/chromium/base/power_monitor/power_monitor_source.cc b/chromium/base/power_monitor/power_monitor_source.cc
index 794eee7b764..dab12ffc2bd 100644
--- a/chromium/base/power_monitor/power_monitor_source.cc
+++ b/chromium/base/power_monitor/power_monitor_source.cc
@@ -17,6 +17,11 @@ bool PowerMonitorSource::IsOnBatteryPower() {
return on_battery_power_;
}
+PowerObserver::DeviceThermalState PowerMonitorSource::GetCurrentThermalState() {
+ return PowerObserver::DeviceThermalState::kUnknown;
+}
+
+// static
void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) {
if (!PowerMonitor::IsInitialized())
return;
@@ -58,6 +63,14 @@ void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) {
}
}
+// static
+void PowerMonitorSource::ProcessThermalEvent(
+ PowerObserver::DeviceThermalState new_thermal_state) {
+ if (!PowerMonitor::IsInitialized())
+ return;
+ PowerMonitor::NotifyThermalStateChange(new_thermal_state);
+}
+
void PowerMonitorSource::SetInitialOnBatteryPowerState(bool on_battery_power) {
// Must only be called before an initialized PowerMonitor exists, otherwise
// the caller should have just used a normal
@@ -66,4 +79,23 @@ void PowerMonitorSource::SetInitialOnBatteryPowerState(bool on_battery_power) {
on_battery_power_ = on_battery_power;
}
+// static
+const char* PowerMonitorSource::DeviceThermalStateToString(
+ PowerObserver::DeviceThermalState state) {
+ switch (state) {
+ case PowerObserver::DeviceThermalState::kUnknown:
+ return "Unknown";
+ case PowerObserver::DeviceThermalState::kNominal:
+ return "Nominal";
+ case PowerObserver::DeviceThermalState::kFair:
+ return "Fair";
+ case PowerObserver::DeviceThermalState::kSerious:
+ return "Serious";
+ case PowerObserver::DeviceThermalState::kCritical:
+ return "Critical";
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
} // namespace base
diff --git a/chromium/base/power_monitor/power_monitor_source.h b/chromium/base/power_monitor/power_monitor_source.h
index 7f59a644026..a80dc59c6de 100644
--- a/chromium/base/power_monitor/power_monitor_source.h
+++ b/chromium/base/power_monitor/power_monitor_source.h
@@ -8,6 +8,7 @@
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/power_monitor/power_observer.h"
#include "base/synchronization/lock.h"
namespace base {
@@ -30,15 +31,24 @@ class BASE_EXPORT PowerMonitorSource {
// Is the computer currently on battery power. Can be called on any thread.
bool IsOnBatteryPower();
+ // Reads the current DeviceThermalState, if available on the platform.
+ // Otherwise, returns kUnknown.
+ virtual PowerObserver::DeviceThermalState GetCurrentThermalState();
+
+ static const char* DeviceThermalStateToString(
+ PowerObserver::DeviceThermalState state);
+
protected:
friend class PowerMonitorTest;
// Friend function that is allowed to access the protected ProcessPowerEvent.
friend void ProcessPowerEventHelper(PowerEvent);
- // ProcessPowerEvent should only be called from a single thread, most likely
+ // Process*Event should only be called from a single thread, most likely
// the UI thread or, in child processes, the IO thread.
static void ProcessPowerEvent(PowerEvent event_id);
+ static void ProcessThermalEvent(
+ PowerObserver::DeviceThermalState new_thermal_state);
// Platform-specific method to check whether the system is currently
// running on battery power. Returns true if running on batteries,
diff --git a/chromium/base/power_monitor/power_monitor_unittest.cc b/chromium/base/power_monitor/power_monitor_unittest.cc
index 2f7e04485d8..db18fbfa9c7 100644
--- a/chromium/base/power_monitor/power_monitor_unittest.cc
+++ b/chromium/base/power_monitor/power_monitor_unittest.cc
@@ -13,12 +13,15 @@ namespace base {
class PowerMonitorTest : public testing::Test {
protected:
- PowerMonitorTest() {
+ PowerMonitorTest() = default;
+
+ void TearDown() override { PowerMonitor::ShutdownForTesting(); }
+
+ void PowerMonitorInitialize() {
power_monitor_source_ = new PowerMonitorTestSource();
PowerMonitor::Initialize(
std::unique_ptr<PowerMonitorSource>(power_monitor_source_));
}
- ~PowerMonitorTest() override { PowerMonitor::ShutdownForTesting(); }
PowerMonitorTestSource* source() { return power_monitor_source_; }
@@ -34,9 +37,11 @@ class PowerMonitorTest : public testing::Test {
TEST_F(PowerMonitorTest, PowerNotifications) {
const int kObservers = 5;
+ PowerMonitorInitialize();
+
PowerMonitorTestObserver observers[kObservers];
for (auto& index : observers)
- EXPECT_TRUE(PowerMonitor::AddObserver(&index));
+ PowerMonitor::AddObserver(&index);
// Sending resume when not suspended should have no effect.
source()->GenerateResumeEvent();
@@ -82,4 +87,53 @@ TEST_F(PowerMonitorTest, PowerNotifications) {
PowerMonitor::RemoveObserver(&index);
}
+TEST_F(PowerMonitorTest, ThermalThrottling) {
+ PowerMonitorTestObserver observer;
+ PowerMonitor::AddObserver(&observer);
+
+ PowerMonitorInitialize();
+
+ constexpr PowerObserver::DeviceThermalState kThermalStates[] = {
+ PowerObserver::DeviceThermalState::kUnknown,
+ PowerObserver::DeviceThermalState::kNominal,
+ PowerObserver::DeviceThermalState::kFair,
+ PowerObserver::DeviceThermalState::kSerious,
+ PowerObserver::DeviceThermalState::kCritical};
+
+ for (const auto state : kThermalStates) {
+ source()->GenerateThermalThrottlingEvent(state);
+ EXPECT_EQ(state, source()->GetCurrentThermalState());
+ EXPECT_EQ(observer.last_thermal_state(), state);
+ }
+
+ PowerMonitor::RemoveObserver(&observer);
+}
+
+TEST_F(PowerMonitorTest, AddObserverBeforeAndAfterInitialization) {
+ PowerMonitorTestObserver observer1;
+ PowerMonitorTestObserver observer2;
+
+ // An observer is added before the PowerMonitor initialization.
+ PowerMonitor::AddObserver(&observer1);
+
+ PowerMonitorInitialize();
+
+ // An observer is added after the PowerMonitor initialization.
+ PowerMonitor::AddObserver(&observer2);
+
+ // Simulate suspend/resume notifications.
+ source()->GenerateSuspendEvent();
+ EXPECT_EQ(observer1.suspends(), 1);
+ EXPECT_EQ(observer2.suspends(), 1);
+ EXPECT_EQ(observer1.resumes(), 0);
+ EXPECT_EQ(observer2.resumes(), 0);
+
+ source()->GenerateResumeEvent();
+ EXPECT_EQ(observer1.resumes(), 1);
+ EXPECT_EQ(observer2.resumes(), 1);
+
+ PowerMonitor::RemoveObserver(&observer1);
+ PowerMonitor::RemoveObserver(&observer2);
+}
+
} // namespace base
diff --git a/chromium/base/power_monitor/power_observer.h b/chromium/base/power_monitor/power_observer.h
index 658172c0ead..e6e3d89fc6b 100644
--- a/chromium/base/power_monitor/power_observer.h
+++ b/chromium/base/power_monitor/power_observer.h
@@ -12,6 +12,21 @@ namespace base {
class BASE_EXPORT PowerObserver {
public:
+ // Values to indicate the system's thermal states: from kNominal onwards to
+ // kCritical they represent increasing SoC die temperatures, usually needing
+ // disruptive actions by the system like e.g. turning on the fans (on systems
+ // equipped with those) or reducing voltage and frequency (oftentimes
+ // degrading overall responsiveness). The taxonomy is derived from MacOS (see
+ // e.g. [1]) but applies to others e.g. Linux/ChromeOS.
+ // [1] https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html
+ enum class DeviceThermalState {
+ kUnknown,
+ kNominal,
+ kFair,
+ kSerious,
+ kCritical,
+ };
+
// Notification of a change in power status of the computer, such
// as from switching between battery and A/C power.
virtual void OnPowerStateChange(bool on_battery_power) {}
@@ -22,6 +37,15 @@ class BASE_EXPORT PowerObserver {
// Notification that the system is resuming.
virtual void OnResume() {}
+ // Notification of a change in the thermal status of the system, such as
+ // entering a critical temperature range. Depending on the severity, the SoC
+ // or the OS might take steps to reduce said temperature e.g., throttling the
+ // CPU or switching on the fans if available. API clients may react to the new
+ // state by reducing expensive computing tasks (e.g. video encoding), or
+ // notifying the user. The same |new_state| might be received repeatedly.
+ // TODO(crbug.com/1071431): implemented on MacOS, extend to Linux/CrOs.
+ virtual void OnThermalStateChange(DeviceThermalState new_state) {}
+
protected:
virtual ~PowerObserver() = default;
};
diff --git a/chromium/base/power_monitor/thermal_state_observer_mac.h b/chromium/base/power_monitor/thermal_state_observer_mac.h
new file mode 100644
index 00000000000..70466a3d8ff
--- /dev/null
+++ b/chromium/base/power_monitor/thermal_state_observer_mac.h
@@ -0,0 +1,40 @@
+// Copyright 2020 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 BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_
+#define BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_
+
+#include <objc/objc.h>
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/power_monitor/power_observer.h"
+
+namespace base {
+
+// This class is used to listen for the thermal state change notification
+// NSProcessInfoThermalStateDidChangeNotification, routing it to
+// PowerMonitorSource.
+class BASE_EXPORT ThermalStateObserverMac {
+ public:
+ using StateUpdateCallback =
+ base::RepeatingCallback<void(PowerObserver::DeviceThermalState)>;
+
+ explicit ThermalStateObserverMac(StateUpdateCallback state_update_callback);
+ ~ThermalStateObserverMac();
+
+ PowerObserver::DeviceThermalState GetCurrentThermalState();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ThermalStateObserverMacTest, StateChange);
+ PowerObserver::DeviceThermalState state_for_testing_ =
+ PowerObserver::DeviceThermalState::kUnknown;
+
+ id thermal_state_update_observer_;
+};
+
+} // namespace base
+
+#endif // BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_
diff --git a/chromium/base/power_monitor/thermal_state_observer_mac.mm b/chromium/base/power_monitor/thermal_state_observer_mac.mm
new file mode 100644
index 00000000000..54b81aaafee
--- /dev/null
+++ b/chromium/base/power_monitor/thermal_state_observer_mac.mm
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "base/power_monitor/thermal_state_observer_mac.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/logging.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace {
+
+base::PowerObserver::DeviceThermalState
+NSProcessInfoThermalStateToDeviceThermalState(
+ NSProcessInfoThermalState nsinfo_state) NS_AVAILABLE_MAC(10_10_3) {
+ switch (nsinfo_state) {
+ case NSProcessInfoThermalStateNominal:
+ return base::PowerObserver::DeviceThermalState::kNominal;
+ case NSProcessInfoThermalStateFair:
+ return base::PowerObserver::DeviceThermalState::kFair;
+ case NSProcessInfoThermalStateSerious:
+ return base::PowerObserver::DeviceThermalState::kSerious;
+ case NSProcessInfoThermalStateCritical:
+ return base::PowerObserver::DeviceThermalState::kCritical;
+ }
+ NOTREACHED();
+ return base::PowerObserver::DeviceThermalState::kUnknown;
+}
+}
+
+namespace base {
+
+ThermalStateObserverMac::ThermalStateObserverMac(
+ StateUpdateCallback state_update_callback) NS_AVAILABLE_MAC(10_10_3) {
+ auto on_state_change_block = ^(NSNotification* notification) {
+ auto state = PowerObserver::DeviceThermalState::kUnknown;
+ // |thermalState| is basically a scale of power usage and its associated
+ // thermal dissipation increase, from Nominal upwards, see:
+ // https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html
+ NSProcessInfoThermalState nsinfo_state =
+ [[NSProcessInfo processInfo] thermalState];
+ state = NSProcessInfoThermalStateToDeviceThermalState(nsinfo_state);
+ if (state_for_testing_ != PowerObserver::DeviceThermalState::kUnknown)
+ state = state_for_testing_;
+ DVLOG(1) << __func__ << ": "
+ << PowerMonitorSource::DeviceThermalStateToString(state);
+ state_update_callback.Run(state);
+ };
+
+ thermal_state_update_observer_ = [[NSNotificationCenter defaultCenter]
+ addObserverForName:NSProcessInfoThermalStateDidChangeNotification
+ object:nil
+ queue:nil
+ usingBlock:on_state_change_block];
+
+ // Force a first call to grab the current status.
+ on_state_change_block(nil);
+}
+
+ThermalStateObserverMac::~ThermalStateObserverMac() {
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:thermal_state_update_observer_];
+}
+
+PowerObserver::DeviceThermalState
+ThermalStateObserverMac::GetCurrentThermalState() NS_AVAILABLE_MAC(10_10_3) {
+ if (state_for_testing_ != PowerObserver::DeviceThermalState::kUnknown)
+ return state_for_testing_;
+ NSProcessInfoThermalState nsinfo_state =
+ [[NSProcessInfo processInfo] thermalState];
+ return NSProcessInfoThermalStateToDeviceThermalState(nsinfo_state);
+}
+}
diff --git a/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm b/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm
new file mode 100644
index 00000000000..3ecaae78e69
--- /dev/null
+++ b/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm
@@ -0,0 +1,52 @@
+// Copyright 2020 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 "base/power_monitor/thermal_state_observer_mac.h"
+
+#include <memory>
+#include <queue>
+
+#import <Foundation/Foundation.h>
+
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using DeviceThermalState = base::PowerObserver::DeviceThermalState;
+
+namespace base {
+
+class ThermalStateObserverMacTest : public testing::Test {
+ public:
+ ThermalStateObserverMacTest() = default;
+ ~ThermalStateObserverMacTest() override = default;
+
+ void OnStateChange(DeviceThermalState state) { state_history_.push(state); }
+
+ std::queue<DeviceThermalState> state_history_;
+ std::unique_ptr<ThermalStateObserverMac> thermal_state_observer_;
+};
+
+// Verifies that a NSProcessInfoThermalStateDidChangeNotification produces the
+// adequate OnStateChange() call.
+TEST_F(ThermalStateObserverMacTest, StateChange) NS_AVAILABLE_MAC(10_10_3) {
+ EXPECT_TRUE(state_history_.empty());
+
+ // ThermalStateObserverMac sends the current thermal state on construction.
+ thermal_state_observer_ =
+ std::make_unique<ThermalStateObserverMac>(BindRepeating(
+ &ThermalStateObserverMacTest::OnStateChange, Unretained(this)));
+ EXPECT_EQ(state_history_.size(), 1u);
+ state_history_.pop();
+
+ thermal_state_observer_->state_for_testing_ = DeviceThermalState::kCritical;
+ [NSNotificationCenter.defaultCenter
+ postNotificationName:NSProcessInfoThermalStateDidChangeNotification
+ object:nil
+ userInfo:nil];
+ EXPECT_EQ(state_history_.size(), 1u);
+ EXPECT_EQ(state_history_.front(), DeviceThermalState::kCritical);
+}
+
+} // namespace base