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/mac/foundation_util.h" | |
12 #include "base/mac/scoped_cftyperef.h" | |
13 #include "base/memory/ref_counted.h" | |
14 #include "base/time/time.h" | |
15 #include "content/public/browser/browser_thread.h" | |
16 #include "third_party/WebKit/public/platform/WebBatteryStatus.h" | |
17 | |
18 namespace content { | |
19 | |
20 namespace { | |
21 | |
22 typedef const BatteryStatusService::BatteryUpdateCallback BatteryCallback; | |
23 | |
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 CFIndexType. | |
27 static CFIndex GetValueAsCFIndex(CFDictionaryRef description, | |
Mark Mentovai
2014/07/24 01:32:22
This is already in an unnamed namespace, so the st
Mark Mentovai
2014/07/24 01:32:22
Why did you choose CFIndex as the type for the CFN
timvolodine
2014/07/24 16:03:19
Done.
timvolodine
2014/07/24 16:03:19
usually it is kCFNumberSInt64Type, but can also be
Mark Mentovai
2014/07/24 16:08:52
timvolodine wrote:
timvolodine
2014/07/24 16:33:42
yes, my point was that the actual values are small
Mark Mentovai
2014/07/24 17:03:46
timvolodine wrote:
timvolodine
2014/07/25 12:46:08
Ok, changed to SInt32.
Technically casting a SInt
Mark Mentovai
2014/07/25 12:57:39
timvolodine wrote:
timvolodine
2014/07/25 13:13:17
right, thanks for the clarification and sorry for
| |
28 CFStringRef key, | |
29 CFIndex default_value) { | |
30 CFNumberRef number = | |
31 base::mac::GetValueFromDictionary<CFNumberRef>(description, key); | |
32 CFIndex value; | |
33 | |
34 if (number && CFNumberGetValue(number, kCFNumberCFIndexType, &value)) | |
35 return value; | |
36 | |
37 return default_value; | |
38 } | |
39 | |
40 static Boolean GetValueAsBoolean(CFDictionaryRef description, | |
Mark Mentovai
2014/07/24 01:32:21
I think that this should return a bool and take a
timvolodine
2014/07/24 16:03:19
Done.
| |
41 CFStringRef key, | |
42 Boolean default_value) { | |
43 CFBooleanRef boolean = | |
44 base::mac::GetValueFromDictionary<CFBooleanRef>(description, key); | |
45 | |
46 return boolean ? CFBooleanGetValue(boolean) : default_value; | |
47 } | |
48 | |
49 static void FetchBatteryStatus(CFDictionaryRef description, | |
50 blink::WebBatteryStatus& status) { | |
51 CFStringRef currentState = | |
Mark Mentovai
2014/07/24 01:32:22
Follow the C++ style guide. This should be named c
timvolodine
2014/07/24 16:03:19
Done.
| |
52 base::mac::GetValueFromDictionary<CFStringRef>(description, | |
53 CFSTR(kIOPSPowerSourceStateKey)); | |
54 | |
55 bool on_battery_power = CFStringCompare(currentState, | |
Mark Mentovai
2014/07/24 01:32:22
I don’t think this is going to be too happy if cur
timvolodine
2014/07/24 16:03:19
actually kIOPSPowerSourceStateKey is a required ke
| |
56 CFSTR(kIOPSBatteryPowerValue), | |
57 0) | |
58 == kCFCompareEqualTo; | |
59 bool is_charging = | |
60 GetValueAsBoolean(description, CFSTR(kIOPSIsChargingKey), true); | |
Mark Mentovai
2014/07/24 01:32:22
Is true the best default here? I probably would ha
timvolodine
2014/07/24 16:03:19
kIOPSIsChargingKey is also required so hopefully n
| |
61 bool is_charged = | |
62 GetValueAsBoolean(description, CFSTR(kIOPSIsChargedKey), false); | |
63 | |
64 // Set charging to false if on battery power and not charging. Otherwise leave | |
65 // the default value, which is true. | |
66 if (on_battery_power && !is_charging) | |
Mark Mentovai
2014/07/24 01:32:22
Why not avoid the conditional and set this explici
timvolodine
2014/07/24 16:03:19
Done.
Re other fields: normally speaking the idea
| |
67 status.charging = false; | |
68 | |
69 CFIndex current_capacity = | |
70 GetValueAsCFIndex(description, CFSTR(kIOPSCurrentCapacityKey), -1); | |
71 CFIndex max_capacity = | |
72 GetValueAsCFIndex(description, CFSTR(kIOPSMaxCapacityKey), -1); | |
73 | |
74 // Set level if it is available and valid. Otherwise leave the default value, | |
75 // which is 1. | |
76 if (current_capacity != -1 && max_capacity != -1 && | |
77 current_capacity <= max_capacity && max_capacity != 0) | |
78 status.level = current_capacity / static_cast<double>(max_capacity); | |
79 | |
80 if (is_charging) { | |
81 CFIndex charging_time = | |
82 GetValueAsCFIndex(description, CFSTR(kIOPSTimeToFullChargeKey), -1); | |
83 | |
84 // Battery is charging: set the charging time if it's available, otherwise | |
85 // set to +infinity. | |
86 status.chargingTime = (charging_time != -1) | |
87 ? base::TimeDelta::FromMinutes(charging_time).InSeconds() | |
88 : std::numeric_limits<double>::infinity(); | |
89 } else { | |
90 // Battery is not charging. | |
91 // Set chargingTime to +infinity if the battery is not charged. Otherwise | |
92 // leave the default value, which is 0. | |
93 if (!is_charged) | |
94 status.chargingTime = std::numeric_limits<double>::infinity(); | |
95 | |
96 // Set dischargingTime if it's available and valid, i.e. when on battery | |
97 // power. Otherwise leave the default value, which is +infinity. | |
98 if (on_battery_power) { | |
99 CFIndex discharging_time = | |
100 GetValueAsCFIndex(description, CFSTR(kIOPSTimeToEmptyKey), -1); | |
101 if (discharging_time != -1) { | |
102 status.dischargingTime = | |
103 base::TimeDelta::FromMinutes(discharging_time).InSeconds(); | |
104 } | |
105 } | |
106 } | |
107 } | |
108 | |
109 static void OnBatteryStatusChanged(BatteryCallback& callback) { | |
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
111 blink::WebBatteryStatus status; | |
112 base::ScopedCFTypeRef<CFTypeRef> blob(IOPSCopyPowerSourcesInfo()); | |
113 base::ScopedCFTypeRef<CFArrayRef> powerSourcesList( | |
Mark Mentovai
2014/07/24 01:32:22
Naming: power_sources_list.
timvolodine
2014/07/24 16:03:19
Done.
| |
114 IOPSCopyPowerSourcesList(blob)); | |
115 CFIndex count = CFArrayGetCount(powerSourcesList); | |
116 | |
117 for (CFIndex i = 0; i < count; ++i) { | |
118 CFDictionaryRef description = IOPSGetPowerSourceDescription(blob, | |
119 CFArrayGetValueAtIndex(powerSourcesList, i)); | |
Mark Mentovai
2014/07/24 01:32:21
if (!description)
continue;
can go here, you
timvolodine
2014/07/24 16:03:19
Done.
| |
120 | |
121 bool battery_present = GetValueAsBoolean(description, | |
122 CFSTR(kIOPSIsPresentKey), | |
123 false); | |
124 | |
125 if (!description || !battery_present) | |
126 continue; | |
127 | |
128 FetchBatteryStatus(description, status); | |
129 break; | |
Mark Mentovai
2014/07/24 01:32:22
In theory, there can be more than one battery. If
timvolodine
2014/07/24 16:03:19
that's right, I've added a comment. I am reluctant
Mark Mentovai
2014/07/24 16:08:52
timvolodine wrote:
timvolodine
2014/07/24 16:33:42
the current specification does not strictly impose
Mark Mentovai
2014/07/24 17:03:46
timvolodine wrote:
timvolodine
2014/07/25 12:46:08
Done.
| |
130 } | |
131 | |
132 callback.Run(status); | |
133 } | |
134 | |
135 class BatteryStatusObserver | |
136 : public base::RefCountedThreadSafe<BatteryStatusObserver> { | |
137 public: | |
138 explicit BatteryStatusObserver(BatteryCallback& callback) | |
139 : callback_(callback) {} | |
140 | |
141 void Start() { | |
142 // Need thread with UI-type message loop for notifications to run. | |
Mark Mentovai
2014/07/24 01:32:21
If you added two more words here, it’d be a comple
timvolodine
2014/07/24 16:03:19
Done.
| |
143 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
144 StartOnUI(); | |
145 } else { | |
146 BrowserThread::PostTask( | |
147 BrowserThread::UI, | |
148 FROM_HERE, | |
149 base::Bind(&BatteryStatusObserver::StartOnUI, this)); | |
150 } | |
151 } | |
152 | |
153 void Stop() { | |
154 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
155 StopOnUI(); | |
156 } else { | |
157 BrowserThread::PostTask( | |
158 BrowserThread::UI, | |
159 FROM_HERE, | |
160 base::Bind(&BatteryStatusObserver::StopOnUI, this)); | |
161 } | |
162 } | |
163 | |
164 private: | |
165 friend class base::RefCountedThreadSafe<BatteryStatusObserver>; | |
166 virtual ~BatteryStatusObserver() { DCHECK(!notifier_run_loop_); } | |
167 | |
168 void StartOnUI() { | |
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
170 | |
171 if (notifier_run_loop_) | |
172 return; | |
173 | |
174 OnBatteryStatusChangedUI((void*)&callback_); | |
Mark Mentovai
2014/07/24 01:32:22
Use cplusplus<style>(casts). See the style guide.
timvolodine
2014/07/24 16:03:19
Done.
| |
175 | |
176 notifier_run_loop_.reset( | |
177 IOPSNotificationCreateRunLoopSource(OnBatteryStatusChangedUI, | |
178 (void*)&callback_)); | |
179 if (notifier_run_loop_) { | |
Mark Mentovai
2014/07/24 01:32:22
Do you need this conditional? If IOPSNotificationC
timvolodine
2014/07/24 16:03:19
I think we should avoid crashing the browser, inst
| |
180 CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_, | |
181 kCFRunLoopDefaultMode); | |
182 } | |
183 } | |
184 | |
185 void StopOnUI() { | |
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
187 | |
188 if (!notifier_run_loop_) | |
189 return; | |
190 | |
191 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_, | |
192 kCFRunLoopDefaultMode); | |
193 notifier_run_loop_.reset(); | |
194 } | |
195 | |
196 static void OnBatteryStatusChangedUI(void* callback) { | |
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
198 // Offload fetching of values and callback execution to the IO thread. | |
199 BrowserThread::PostTask( | |
200 BrowserThread::IO, | |
201 FROM_HERE, | |
202 base::Bind(&OnBatteryStatusChanged, | |
203 *static_cast<BatteryCallback*>(callback))); | |
204 } | |
205 | |
206 BatteryCallback callback_; | |
207 base::ScopedCFTypeRef<CFRunLoopSourceRef> notifier_run_loop_; | |
208 | |
209 DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); | |
210 }; | |
211 | |
212 class BatteryStatusManagerMac : public BatteryStatusManager { | |
213 public: | |
214 explicit BatteryStatusManagerMac(BatteryCallback& callback) | |
215 : notifier_(new BatteryStatusObserver(callback)) {} | |
216 | |
217 virtual ~BatteryStatusManagerMac() { | |
218 notifier_->Stop(); | |
219 } | |
220 | |
221 // BatteryStatusManager: | |
222 virtual bool StartListeningBatteryChange() OVERRIDE { | |
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
224 notifier_->Start(); | |
225 return true; | |
226 } | |
227 | |
228 virtual void StopListeningBatteryChange() OVERRIDE { | |
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
230 notifier_->Stop(); | |
231 } | |
232 | |
233 private: | |
234 scoped_refptr<BatteryStatusObserver> notifier_; | |
235 | |
236 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerMac); | |
237 }; | |
238 | |
239 } // end namespace | |
240 | |
241 // static | |
242 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
243 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
244 return scoped_ptr<BatteryStatusManager>( | |
245 new BatteryStatusManagerMac(callback)); | |
246 } | |
247 | |
248 } // namespace content | |
OLD | NEW |