[Telemetry] Add PRESUBMIT prevent people from using GetChromiumSrcDir() in telemetry.
[chromium-blink-merge.git] / device / battery / battery_status_manager_mac.cc
blob8481c4726c84b485ab94e7a7ed77281a919b5d44
1 // Copyright 2014 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.
5 #include "device/battery/battery_status_manager.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <IOKit/ps/IOPowerSources.h>
9 #include <IOKit/ps/IOPSKeys.h>
10 #include <vector>
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/metrics/histogram.h"
16 #include "base/time/time.h"
18 namespace device {
20 namespace {
22 typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback;
24 // Returns the value corresponding to |key| in the dictionary |description|.
25 // Returns |default_value| if the dictionary does not contain |key|, the
26 // corresponding value is NULL or it could not be converted to SInt64.
27 SInt64 GetValueAsSInt64(CFDictionaryRef description,
28 CFStringRef key,
29 SInt64 default_value) {
30 CFNumberRef number =
31 base::mac::GetValueFromDictionary<CFNumberRef>(description, key);
32 SInt64 value;
34 if (number && CFNumberGetValue(number, kCFNumberSInt64Type, &value))
35 return value;
37 return default_value;
40 bool GetValueAsBoolean(CFDictionaryRef description,
41 CFStringRef key,
42 bool default_value) {
43 CFBooleanRef boolean =
44 base::mac::GetValueFromDictionary<CFBooleanRef>(description, key);
46 return boolean ? CFBooleanGetValue(boolean) : default_value;
49 bool CFStringsAreEqual(CFStringRef string1, CFStringRef string2) {
50 if (!string1 || !string2)
51 return false;
52 return CFStringCompare(string1, string2, 0) == kCFCompareEqualTo;
55 void UpdateNumberBatteriesHistogram(int count) {
56 UMA_HISTOGRAM_CUSTOM_COUNTS(
57 "BatteryStatus.NumberBatteriesMac", count, 1, 5, 6);
60 void FetchBatteryStatus(CFDictionaryRef description, BatteryStatus* status) {
61 CFStringRef current_state =
62 base::mac::GetValueFromDictionary<CFStringRef>(description,
63 CFSTR(kIOPSPowerSourceStateKey));
65 bool on_battery_power =
66 CFStringsAreEqual(current_state, CFSTR(kIOPSBatteryPowerValue));
67 bool is_charging =
68 GetValueAsBoolean(description, CFSTR(kIOPSIsChargingKey), true);
69 bool is_charged =
70 GetValueAsBoolean(description, CFSTR(kIOPSIsChargedKey), false);
72 status->charging = !on_battery_power || is_charging;
74 SInt64 current_capacity =
75 GetValueAsSInt64(description, CFSTR(kIOPSCurrentCapacityKey), -1);
76 SInt64 max_capacity =
77 GetValueAsSInt64(description, CFSTR(kIOPSMaxCapacityKey), -1);
79 // Set level if it is available and valid. Otherwise leave the default value,
80 // which is 1.
81 if (current_capacity != -1 && max_capacity != -1 &&
82 current_capacity <= max_capacity && max_capacity != 0) {
83 status->level = current_capacity / static_cast<double>(max_capacity);
86 if (is_charging) {
87 SInt64 charging_time =
88 GetValueAsSInt64(description, CFSTR(kIOPSTimeToFullChargeKey), -1);
90 // Battery is charging: set the charging time if it's available, otherwise
91 // set to +infinity.
92 status->charging_time = charging_time != -1
93 ? base::TimeDelta::FromMinutes(charging_time).InSeconds()
94 : std::numeric_limits<double>::infinity();
95 } else {
96 // Battery is not charging.
97 // Set chargingTime to +infinity if the battery is not charged. Otherwise
98 // leave the default value, which is 0.
99 if (!is_charged)
100 status->charging_time = std::numeric_limits<double>::infinity();
102 // Set dischargingTime if it's available and valid, i.e. when on battery
103 // power. Otherwise leave the default value, which is +infinity.
104 if (on_battery_power) {
105 SInt64 discharging_time =
106 GetValueAsSInt64(description, CFSTR(kIOPSTimeToEmptyKey), -1);
107 if (discharging_time != -1) {
108 status->discharging_time =
109 base::TimeDelta::FromMinutes(discharging_time).InSeconds();
115 std::vector<BatteryStatus> GetInternalBatteriesStates() {
116 std::vector<BatteryStatus> internal_sources;
118 base::ScopedCFTypeRef<CFTypeRef> info(IOPSCopyPowerSourcesInfo());
119 base::ScopedCFTypeRef<CFArrayRef> power_sources_list(
120 IOPSCopyPowerSourcesList(info));
121 CFIndex count = CFArrayGetCount(power_sources_list);
123 for (CFIndex i = 0; i < count; ++i) {
124 CFDictionaryRef description = IOPSGetPowerSourceDescription(info,
125 CFArrayGetValueAtIndex(power_sources_list, i));
127 if (!description)
128 continue;
130 CFStringRef transport_type =
131 base::mac::GetValueFromDictionary<CFStringRef>(description,
132 CFSTR(kIOPSTransportTypeKey));
134 bool internal_source =
135 CFStringsAreEqual(transport_type, CFSTR(kIOPSInternalType));
136 bool source_present =
137 GetValueAsBoolean(description, CFSTR(kIOPSIsPresentKey), false);
139 if (internal_source && source_present) {
140 BatteryStatus status;
141 FetchBatteryStatus(description, &status);
142 internal_sources.push_back(status);
146 return internal_sources;
149 void OnBatteryStatusChanged(const BatteryCallback& callback) {
150 std::vector<BatteryStatus> batteries(GetInternalBatteriesStates());
152 if (batteries.empty()) {
153 callback.Run(BatteryStatus());
154 return;
157 // TODO(timvolodine): implement the case when there are multiple internal
158 // sources, e.g. when multiple batteries are present. Currently this will
159 // fail a DCHECK.
160 DCHECK(batteries.size() == 1);
161 callback.Run(batteries.front());
164 class BatteryStatusObserver {
165 public:
166 explicit BatteryStatusObserver(const BatteryCallback& callback)
167 : callback_(callback) {}
169 ~BatteryStatusObserver() { DCHECK(!notifier_run_loop_source_); }
171 void Start() {
172 if (notifier_run_loop_source_)
173 return;
175 notifier_run_loop_source_.reset(
176 IOPSNotificationCreateRunLoopSource(CallOnBatteryStatusChanged,
177 static_cast<void*>(&callback_)));
178 if (!notifier_run_loop_source_) {
179 LOG(ERROR) << "Failed to create battery status notification run loop";
180 // Make sure to execute to callback with the default values.
181 callback_.Run(BatteryStatus());
182 return;
185 CallOnBatteryStatusChanged(static_cast<void*>(&callback_));
186 CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
187 kCFRunLoopDefaultMode);
188 UpdateNumberBatteriesHistogram(GetInternalBatteriesStates().size());
191 void Stop() {
192 if (!notifier_run_loop_source_)
193 return;
195 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
196 kCFRunLoopDefaultMode);
197 notifier_run_loop_source_.reset();
200 private:
201 static void CallOnBatteryStatusChanged(void* callback) {
202 OnBatteryStatusChanged(*static_cast<BatteryCallback*>(callback));
205 BatteryCallback callback_;
206 base::ScopedCFTypeRef<CFRunLoopSourceRef> notifier_run_loop_source_;
208 DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver);
211 class BatteryStatusManagerMac : public BatteryStatusManager {
212 public:
213 explicit BatteryStatusManagerMac(const BatteryCallback& callback)
214 : notifier_(new BatteryStatusObserver(callback)) {}
216 ~BatteryStatusManagerMac() override { notifier_->Stop(); }
218 // BatteryStatusManager:
219 bool StartListeningBatteryChange() override {
220 notifier_->Start();
221 return true;
224 void StopListeningBatteryChange() override {
225 notifier_->Stop();
228 private:
229 scoped_ptr<BatteryStatusObserver> notifier_;
231 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerMac);
234 } // end namespace
236 // static
237 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
238 const BatteryStatusService::BatteryUpdateCallback& callback) {
239 return scoped_ptr<BatteryStatusManager>(
240 new BatteryStatusManagerMac(callback));
243 } // namespace device