// Copyright 2013 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. // Implementation based on sample code from // http://developer.apple.com/library/mac/#qa/qa1340/_index.html. #include "base/power_monitor/power_monitor_device_source.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_source.h" #include #include #include #include namespace base { void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) { PowerMonitorSource::ProcessPowerEvent(event); } bool PowerMonitorDeviceSource::IsOnBatteryPower() { base::ScopedCFTypeRef info(IOPSCopyPowerSourcesInfo()); if (!info) return false; base::ScopedCFTypeRef power_sources_list( IOPSCopyPowerSourcesList(info)); if (!power_sources_list) return false; bool found_battery = false; const CFIndex count = CFArrayGetCount(power_sources_list); for (CFIndex i = 0; i < count; ++i) { const CFDictionaryRef description = IOPSGetPowerSourceDescription( info, CFArrayGetValueAtIndex(power_sources_list, i)); if (!description) continue; CFStringRef current_state = base::mac::GetValueFromDictionary( description, CFSTR(kIOPSPowerSourceStateKey)); if (!current_state) continue; // We only report "on battery power" if no source is on AC power. if (CFStringCompare(current_state, CFSTR(kIOPSOffLineValue), 0) == kCFCompareEqualTo) { continue; } else if (CFStringCompare(current_state, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo) { return false; } DCHECK_EQ(CFStringCompare(current_state, CFSTR(kIOPSBatteryPowerValue), 0), kCFCompareEqualTo) << "Power source state is not one of 3 documented values"; found_battery = true; } // At this point, either there were no readable batteries found, in which case // this Mac is not on battery power, or all the readable batteries were on // battery power, in which case, count this as being on battery power. return found_battery; } PowerThermalObserver::DeviceThermalState PowerMonitorDeviceSource::GetCurrentThermalState() { return thermal_state_observer_->GetCurrentThermalState(); } int PowerMonitorDeviceSource::GetInitialSpeedLimit() { return thermal_state_observer_->GetCurrentSpeedLimit(); } namespace { void BatteryEventCallback(void*) { ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT); } } // namespace void PowerMonitorDeviceSource::PlatformInit() { power_manager_port_ = IORegisterForSystemPower( this, mac::ScopedIONotificationPortRef::Receiver(notification_port_).get(), &SystemPowerEventCallback, ¬ifier_); DCHECK_NE(power_manager_port_, IO_OBJECT_NULL); // Add the sleep/wake notification event source to the runloop. CFRunLoopAddSource( CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notification_port_.get()), kCFRunLoopCommonModes); // Create and add the power-source-change event source to the runloop. power_source_run_loop_source_.reset( IOPSNotificationCreateRunLoopSource(&BatteryEventCallback, nullptr)); // Verify that the source was created. This may fail if the sandbox does not // permit the process to access the underlying system service. See // https://crbug.com/897557 for an example of such a configuration bug. DCHECK(power_source_run_loop_source_); CFRunLoopAddSource(CFRunLoopGetCurrent(), power_source_run_loop_source_, kCFRunLoopDefaultMode); thermal_state_observer_ = std::make_unique( BindRepeating(&PowerMonitorSource::ProcessThermalEvent), BindRepeating(&PowerMonitorSource::ProcessSpeedLimitEvent)); } void PowerMonitorDeviceSource::PlatformDestroy() { CFRunLoopRemoveSource( CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notification_port_.get()), kCFRunLoopCommonModes); CFRunLoopRemoveSource(CFRunLoopGetCurrent(), power_source_run_loop_source_.get(), kCFRunLoopDefaultMode); // Deregister for system power notifications. IODeregisterForSystemPower(¬ifier_); // Close the connection to the IOPMrootDomain that was opened in // PlatformInit(). IOServiceClose(power_manager_port_); power_manager_port_ = IO_OBJECT_NULL; } void PowerMonitorDeviceSource::SystemPowerEventCallback( void* refcon, io_service_t service, natural_t message_type, void* message_argument) { auto* thiz = static_cast(refcon); switch (message_type) { // If this message is not handled the system may delay sleep for 30 seconds. case kIOMessageCanSystemSleep: IOAllowPowerChange(thiz->power_manager_port_, reinterpret_cast(message_argument)); break; case kIOMessageSystemWillSleep: ProcessPowerEventHelper(PowerMonitorSource::SUSPEND_EVENT); IOAllowPowerChange(thiz->power_manager_port_, reinterpret_cast(message_argument)); break; case kIOMessageSystemWillPowerOn: ProcessPowerEventHelper(PowerMonitorSource::RESUME_EVENT); break; } } } // namespace base