power_monitor_device_source_mac.mm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright 2013 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. // Implementation based on sample code from
  5. // http://developer.apple.com/library/mac/#qa/qa1340/_index.html.
  6. #include "base/power_monitor/power_monitor_device_source.h"
  7. #include "base/mac/foundation_util.h"
  8. #include "base/mac/scoped_cftyperef.h"
  9. #include "base/power_monitor/power_monitor.h"
  10. #include "base/power_monitor/power_monitor_source.h"
  11. #include <IOKit/IOMessage.h>
  12. #include <IOKit/ps/IOPSKeys.h>
  13. #include <IOKit/ps/IOPowerSources.h>
  14. #include <IOKit/pwr_mgt/IOPMLib.h>
  15. namespace base {
  16. void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) {
  17. PowerMonitorSource::ProcessPowerEvent(event);
  18. }
  19. bool PowerMonitorDeviceSource::IsOnBatteryPower() {
  20. base::ScopedCFTypeRef<CFTypeRef> info(IOPSCopyPowerSourcesInfo());
  21. if (!info)
  22. return false;
  23. base::ScopedCFTypeRef<CFArrayRef> power_sources_list(
  24. IOPSCopyPowerSourcesList(info));
  25. if (!power_sources_list)
  26. return false;
  27. bool found_battery = false;
  28. const CFIndex count = CFArrayGetCount(power_sources_list);
  29. for (CFIndex i = 0; i < count; ++i) {
  30. const CFDictionaryRef description = IOPSGetPowerSourceDescription(
  31. info, CFArrayGetValueAtIndex(power_sources_list, i));
  32. if (!description)
  33. continue;
  34. CFStringRef current_state = base::mac::GetValueFromDictionary<CFStringRef>(
  35. description, CFSTR(kIOPSPowerSourceStateKey));
  36. if (!current_state)
  37. continue;
  38. // We only report "on battery power" if no source is on AC power.
  39. if (CFStringCompare(current_state, CFSTR(kIOPSOffLineValue), 0) ==
  40. kCFCompareEqualTo) {
  41. continue;
  42. } else if (CFStringCompare(current_state, CFSTR(kIOPSACPowerValue), 0) ==
  43. kCFCompareEqualTo) {
  44. return false;
  45. }
  46. DCHECK_EQ(CFStringCompare(current_state, CFSTR(kIOPSBatteryPowerValue), 0),
  47. kCFCompareEqualTo)
  48. << "Power source state is not one of 3 documented values";
  49. found_battery = true;
  50. }
  51. // At this point, either there were no readable batteries found, in which case
  52. // this Mac is not on battery power, or all the readable batteries were on
  53. // battery power, in which case, count this as being on battery power.
  54. return found_battery;
  55. }
  56. PowerThermalObserver::DeviceThermalState
  57. PowerMonitorDeviceSource::GetCurrentThermalState() {
  58. return thermal_state_observer_->GetCurrentThermalState();
  59. }
  60. int PowerMonitorDeviceSource::GetInitialSpeedLimit() {
  61. return thermal_state_observer_->GetCurrentSpeedLimit();
  62. }
  63. namespace {
  64. void BatteryEventCallback(void*) {
  65. ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT);
  66. }
  67. } // namespace
  68. void PowerMonitorDeviceSource::PlatformInit() {
  69. power_manager_port_ = IORegisterForSystemPower(
  70. this,
  71. mac::ScopedIONotificationPortRef::Receiver(notification_port_).get(),
  72. &SystemPowerEventCallback, &notifier_);
  73. DCHECK_NE(power_manager_port_, IO_OBJECT_NULL);
  74. // Add the sleep/wake notification event source to the runloop.
  75. CFRunLoopAddSource(
  76. CFRunLoopGetCurrent(),
  77. IONotificationPortGetRunLoopSource(notification_port_.get()),
  78. kCFRunLoopCommonModes);
  79. // Create and add the power-source-change event source to the runloop.
  80. power_source_run_loop_source_.reset(
  81. IOPSNotificationCreateRunLoopSource(&BatteryEventCallback, nullptr));
  82. // Verify that the source was created. This may fail if the sandbox does not
  83. // permit the process to access the underlying system service. See
  84. // https://crbug.com/897557 for an example of such a configuration bug.
  85. DCHECK(power_source_run_loop_source_);
  86. CFRunLoopAddSource(CFRunLoopGetCurrent(), power_source_run_loop_source_,
  87. kCFRunLoopDefaultMode);
  88. thermal_state_observer_ = std::make_unique<ThermalStateObserverMac>(
  89. BindRepeating(&PowerMonitorSource::ProcessThermalEvent),
  90. BindRepeating(&PowerMonitorSource::ProcessSpeedLimitEvent));
  91. }
  92. void PowerMonitorDeviceSource::PlatformDestroy() {
  93. CFRunLoopRemoveSource(
  94. CFRunLoopGetCurrent(),
  95. IONotificationPortGetRunLoopSource(notification_port_.get()),
  96. kCFRunLoopCommonModes);
  97. CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
  98. power_source_run_loop_source_.get(),
  99. kCFRunLoopDefaultMode);
  100. // Deregister for system power notifications.
  101. IODeregisterForSystemPower(&notifier_);
  102. // Close the connection to the IOPMrootDomain that was opened in
  103. // PlatformInit().
  104. IOServiceClose(power_manager_port_);
  105. power_manager_port_ = IO_OBJECT_NULL;
  106. }
  107. void PowerMonitorDeviceSource::SystemPowerEventCallback(
  108. void* refcon,
  109. io_service_t service,
  110. natural_t message_type,
  111. void* message_argument) {
  112. auto* thiz = static_cast<PowerMonitorDeviceSource*>(refcon);
  113. switch (message_type) {
  114. // If this message is not handled the system may delay sleep for 30 seconds.
  115. case kIOMessageCanSystemSleep:
  116. IOAllowPowerChange(thiz->power_manager_port_,
  117. reinterpret_cast<intptr_t>(message_argument));
  118. break;
  119. case kIOMessageSystemWillSleep:
  120. ProcessPowerEventHelper(PowerMonitorSource::SUSPEND_EVENT);
  121. IOAllowPowerChange(thiz->power_manager_port_,
  122. reinterpret_cast<intptr_t>(message_argument));
  123. break;
  124. case kIOMessageSystemWillPowerOn:
  125. ProcessPowerEventHelper(PowerMonitorSource::RESUME_EVENT);
  126. break;
  127. }
  128. }
  129. } // namespace base