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