OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "content/browser/battery_status/battery_status_manager.h" | |
6 | |
7 #include <CoreFoundation/CoreFoundation.h> | |
8 #include <IOKit/ps/IOPowerSources.h> | |
9 #include <IOKit/ps/IOPSKeys.h> | |
10 | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/threading/thread.h" | |
13 #include "base/time/time.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "third_party/WebKit/public/platform/WebBatteryStatus.h" | |
16 | |
17 namespace content { | |
18 | |
19 namespace { | |
20 | |
21 typedef const BatteryStatusService::BatteryUpdateCallback BatteryCallback; | |
22 | |
23 // Returns the value corresponding to |key| in the dictionary |description|. | |
24 // Returns |default_value| if the dictionary does not contain |key|, the | |
25 // corresponding value is NULL or it could not be converted to CFIndexType. | |
26 static CFIndex GetValueAsCFIndex(CFDictionaryRef description, | |
27 CFStringRef key, | |
28 CFIndex default_value) { | |
29 CFNumberRef value_number = | |
30 (CFNumberRef)CFDictionaryGetValue(description, key); | |
Mark Mentovai
2014/07/17 14:08:31
CFCast<CFNumberRef>.
Robert Sesek
2014/07/17 15:16:41
Better yet: base::mac::GetValueFromDictionary<T>(d
timvolodine
2014/07/22 18:04:43
Done.
timvolodine
2014/07/22 18:04:43
Done.
| |
31 CFIndex value; | |
32 | |
33 if (value_number && | |
34 CFNumberGetValue(value_number, kCFNumberCFIndexType, &value)) | |
35 return value; | |
36 | |
37 return default_value; | |
38 } | |
39 | |
40 static void FetchBatteryStatus(CFDictionaryRef description, | |
41 blink::WebBatteryStatus& status) { | |
42 CFStringRef currentState = | |
43 (CFStringRef)CFDictionaryGetValue(description, | |
Mark Mentovai
2014/07/17 14:08:31
CFCast<CFStringRef>.
timvolodine
2014/07/22 18:04:44
Done.
| |
44 CFSTR(kIOPSPowerSourceStateKey)); | |
45 bool on_battery_power = | |
46 CFStringCompare(currentState, CFSTR(kIOPSBatteryPowerValue), 0) | |
47 == kCFCompareEqualTo; | |
48 bool is_charging = | |
49 CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) | |
Mark Mentovai
2014/07/17 14:08:31
This just tells you if kIOPSIsChargingKey was foun
timvolodine
2014/07/22 18:04:44
I've changed this to use CFBooleanGetValue, as you
| |
50 == kCFBooleanTrue; | |
51 bool is_charged = | |
52 CFDictionaryGetValue(description, CFSTR(kIOPSIsChargedKey)) | |
53 == kCFBooleanTrue; | |
54 | |
55 // Set charging to false if on battery power and not charging. | |
Mark Mentovai
2014/07/17 14:08:31
Why not set status.charging to is_charging?
Why h
timvolodine
2014/07/22 18:04:44
yes this is on purpose. I've added a comment expla
| |
56 if (on_battery_power && !is_charging) | |
57 status.charging = false; | |
58 | |
59 CFIndex current_capacity = | |
60 GetValueAsCFIndex(description, CFSTR(kIOPSCurrentCapacityKey), -1); | |
61 CFIndex max_capacity = | |
62 GetValueAsCFIndex(description, CFSTR(kIOPSMaxCapacityKey), -1); | |
63 | |
64 if (current_capacity != -1 && max_capacity != -1) | |
Mark Mentovai
2014/07/17 14:08:31
&& current_capacity <= max_capacity?
&& max_capaci
timvolodine
2014/07/22 18:04:44
Done.
| |
65 status.level = (double)current_capacity/(double)max_capacity; | |
Mark Mentovai
2014/07/17 14:08:31
Don’t use (C_style)casts, use Cplusplus<style>(cas
timvolodine
2014/07/22 18:04:43
Done.
| |
66 | |
67 if (is_charging) { | |
68 CFIndex charging_time = | |
69 GetValueAsCFIndex(description, CFSTR(kIOPSTimeToFullChargeKey), -1); | |
70 | |
71 // Battery is charging: set the charging time if it's available, otherwise | |
72 // set to +infinity. | |
73 status.chargingTime = (charging_time != -1) | |
Mark Mentovai
2014/07/17 14:08:31
WebBatteryStatus.h doesn’t have comments in it to
timvolodine
2014/07/22 18:04:44
The specification is here: https://dvcs.w3.org/hg/
| |
74 ? base::TimeDelta::FromMinutes(charging_time).InSeconds() | |
75 : std::numeric_limits<double>::infinity(); | |
76 } else { | |
77 // Battery is not charging. | |
78 // Set chargingTime to +infinity if the battery is not charged. | |
79 if (!is_charged) | |
80 status.chargingTime = std::numeric_limits<double>::infinity(); | |
81 | |
82 // Set dischargingTime if it's available and valid, i.e. when on battery | |
83 // power. | |
84 if (on_battery_power) { | |
85 CFIndex discharging_time = | |
86 GetValueAsCFIndex(description, CFSTR(kIOPSTimeToEmptyKey), -1); | |
87 if (discharging_time != -1) { | |
88 status.dischargingTime = | |
Mark Mentovai
2014/07/17 14:08:31
Should you be setting status.dischargingTime to so
timvolodine
2014/07/22 18:04:44
no garbage ;). I've added some comments regarding
| |
89 base::TimeDelta::FromMinutes(discharging_time).InSeconds(); | |
90 } | |
91 } | |
92 } | |
93 } | |
94 | |
95 static void OnBatteryStatusChanged(BatteryCallback& callback) { | |
96 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
97 blink::WebBatteryStatus status; | |
98 CFTypeRef blob = IOPSCopyPowerSourcesInfo(); | |
Mark Mentovai
2014/07/17 14:08:31
Do you need to release this or does IOPSCopyPowerS
timvolodine
2014/07/22 18:04:44
you are right, it needs to be released. added Scop
| |
99 CFArrayRef powerSourcesList = IOPSCopyPowerSourcesList(blob); | |
Mark Mentovai
2014/07/17 14:08:31
This one almost definitely needs a ScopedCFTypeRef
timvolodine
2014/07/22 18:04:44
added ScopedCFTypeRef, done.
| |
100 CFIndex count = CFArrayGetCount(powerSourcesList); | |
101 | |
102 for (CFIndex i = 0; i < count; ++i) { | |
103 CFDictionaryRef description = IOPSGetPowerSourceDescription(blob, | |
104 CFArrayGetValueAtIndex(powerSourcesList, i)); | |
105 | |
106 bool battery_present = | |
107 CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) | |
Mark Mentovai
2014/07/17 14:08:31
CFDictionaryGetValue doesn’t return kCFBooleanTrue
timvolodine
2014/07/22 18:04:44
Done. (but it looks like it actually returns a poi
| |
108 == kCFBooleanTrue; | |
109 | |
110 if (!description || !battery_present) | |
111 continue; | |
112 | |
113 FetchBatteryStatus(description, status); | |
114 break; | |
115 } | |
116 | |
117 callback.Run(status); | |
118 } | |
119 | |
120 class BatteryStatusObserver | |
121 : public base::RefCountedThreadSafe<BatteryStatusObserver> { | |
122 public: | |
123 explicit BatteryStatusObserver(BatteryCallback& callback) | |
124 : callback_(callback), notifier_run_loop_(0) {} | |
Mark Mentovai
2014/07/17 14:08:31
Use NULL for null CFTypeRef-family values here and
timvolodine
2014/07/22 18:04:43
not necessary anymore because I made notifier_run_
| |
125 | |
126 void Start() { | |
127 // Need thread with UI-type message loop for notifications to run. | |
128 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
129 StartOnUI(); | |
130 } else { | |
131 BrowserThread::PostTask( | |
132 BrowserThread::UI, | |
133 FROM_HERE, | |
134 base::Bind(&BatteryStatusObserver::StartOnUI, this)); | |
135 } | |
136 } | |
137 | |
138 void Stop() { | |
139 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
140 StopOnUI(); | |
141 } else { | |
142 BrowserThread::PostTask( | |
143 BrowserThread::UI, | |
144 FROM_HERE, | |
145 base::Bind(&BatteryStatusObserver::StopOnUI, this)); | |
146 } | |
147 } | |
148 | |
149 private: | |
150 friend class base::RefCountedThreadSafe<BatteryStatusObserver>; | |
151 virtual ~BatteryStatusObserver() {} | |
Mark Mentovai
2014/07/17 14:08:31
[D]CHECK that it’s stopped (by notifier_run_loop_
timvolodine
2014/07/22 18:04:44
Done.
| |
152 | |
153 void StartOnUI() { | |
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
155 | |
156 if (notifier_run_loop_) | |
Mark Mentovai
2014/07/17 14:08:31
Might this actually happen in practice? It may be
timvolodine
2014/07/22 18:04:44
I don't think we should be that strict, users may
| |
157 return; | |
158 | |
159 OnBatteryStatusChangedUI((void*)&callback_); | |
160 | |
161 notifier_run_loop_ = | |
162 IOPSNotificationCreateRunLoopSource(OnBatteryStatusChangedUI, | |
163 (void*)&callback_); | |
164 if (notifier_run_loop_) { | |
165 CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_, | |
166 kCFRunLoopDefaultMode); | |
167 } | |
168 } | |
169 | |
170 void StopOnUI() { | |
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
172 | |
173 if (!notifier_run_loop_) | |
174 return; | |
175 | |
176 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_, | |
177 kCFRunLoopDefaultMode); | |
178 CFRelease(notifier_run_loop_); | |
179 notifier_run_loop_ = 0; | |
180 } | |
181 | |
182 static void OnBatteryStatusChangedUI(void* callback) { | |
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
184 // Offload fetching of values and callback execution to the IO thread. | |
185 BrowserThread::PostTask( | |
186 BrowserThread::IO, | |
187 FROM_HERE, | |
188 base::Bind(&OnBatteryStatusChanged, | |
189 *static_cast<BatteryCallback*>(callback))); | |
190 } | |
191 | |
192 BatteryCallback& callback_; | |
Robert Sesek
2014/07/17 15:16:41
Why is this stored by reference?
timvolodine
2014/07/22 18:04:43
technically this is ok because the callback is own
| |
193 CFRunLoopSourceRef notifier_run_loop_; | |
194 | |
195 DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); | |
196 }; | |
197 | |
198 class BatteryStatusManagerMac : public BatteryStatusManager { | |
199 public: | |
200 explicit BatteryStatusManagerMac(BatteryCallback& callback) | |
201 : notifier_(new BatteryStatusObserver(callback)) {} | |
202 | |
203 virtual ~BatteryStatusManagerMac() { | |
204 notifier_->Stop(); | |
205 } | |
206 | |
207 // BatteryStatusManager: | |
208 virtual bool StartListeningBatteryChange() OVERRIDE { | |
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
210 notifier_->Start(); | |
211 return true; | |
212 } | |
213 | |
214 virtual void StopListeningBatteryChange() OVERRIDE { | |
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
216 notifier_->Stop(); | |
217 } | |
218 | |
219 private: | |
220 scoped_refptr<BatteryStatusObserver> notifier_; | |
221 | |
222 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerMac); | |
223 }; | |
224 | |
225 } // end namespace | |
226 | |
227 // static | |
228 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
229 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
230 return scoped_ptr<BatteryStatusManager>( | |
231 new BatteryStatusManagerMac(callback)); | |
232 } | |
233 | |
234 } // namespace content | |
OLD | NEW |