Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / device / battery / battery_status_manager_mac.cc
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 "device/battery/battery_status_manager.h"
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <IOKit/ps/IOPowerSources.h>
9 #include <IOKit/ps/IOPSKeys.h>
10 #include <vector>
11
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"
17
18 namespace device {
19
20 namespace {
21
22 typedef 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 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;
33
34   if (number && CFNumberGetValue(number, kCFNumberSInt64Type, &value))
35     return value;
36
37   return default_value;
38 }
39
40 bool GetValueAsBoolean(CFDictionaryRef description,
41                        CFStringRef key,
42                        bool default_value) {
43   CFBooleanRef boolean =
44       base::mac::GetValueFromDictionary<CFBooleanRef>(description, key);
45
46   return boolean ? CFBooleanGetValue(boolean) : default_value;
47 }
48
49 bool CFStringsAreEqual(CFStringRef string1, CFStringRef string2) {
50   if (!string1 || !string2)
51     return false;
52   return CFStringCompare(string1, string2, 0) == kCFCompareEqualTo;
53 }
54
55 void UpdateNumberBatteriesHistogram(int count) {
56   UMA_HISTOGRAM_CUSTOM_COUNTS(
57       "BatteryStatus.NumberBatteriesMac", count, 1, 5, 6);
58 }
59
60 void FetchBatteryStatus(CFDictionaryRef description, BatteryStatus* status) {
61   CFStringRef current_state =
62       base::mac::GetValueFromDictionary<CFStringRef>(description,
63           CFSTR(kIOPSPowerSourceStateKey));
64
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);
71
72   status->charging = !on_battery_power || is_charging;
73
74   SInt64 current_capacity =
75       GetValueAsSInt64(description, CFSTR(kIOPSCurrentCapacityKey), -1);
76   SInt64 max_capacity =
77       GetValueAsSInt64(description, CFSTR(kIOPSMaxCapacityKey), -1);
78
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);
84   }
85
86   if (is_charging) {
87     SInt64 charging_time =
88         GetValueAsSInt64(description, CFSTR(kIOPSTimeToFullChargeKey), -1);
89
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();
101
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();
110       }
111     }
112   }
113 }
114
115 std::vector<BatteryStatus> GetInternalBatteriesStates() {
116   std::vector<BatteryStatus> internal_sources;
117
118   base::ScopedCFTypeRef<CFTypeRef> info(IOPSCopyPowerSourcesInfo());
119   base::ScopedCFTypeRef<CFArrayRef> power_sources_list(
120       IOPSCopyPowerSourcesList(info));
121   CFIndex count = CFArrayGetCount(power_sources_list);
122
123   for (CFIndex i = 0; i < count; ++i) {
124     CFDictionaryRef description = IOPSGetPowerSourceDescription(info,
125         CFArrayGetValueAtIndex(power_sources_list, i));
126
127     if (!description)
128       continue;
129
130     CFStringRef transport_type =
131         base::mac::GetValueFromDictionary<CFStringRef>(description,
132             CFSTR(kIOPSTransportTypeKey));
133
134     bool internal_source =
135         CFStringsAreEqual(transport_type, CFSTR(kIOPSInternalType));
136     bool source_present =
137         GetValueAsBoolean(description, CFSTR(kIOPSIsPresentKey), false);
138
139     if (internal_source && source_present) {
140       BatteryStatus status;
141       FetchBatteryStatus(description, &status);
142       internal_sources.push_back(status);
143     }
144   }
145
146   return internal_sources;
147 }
148
149 void OnBatteryStatusChanged(const BatteryCallback& callback) {
150   std::vector<BatteryStatus> batteries(GetInternalBatteriesStates());
151
152   if (batteries.empty()) {
153     callback.Run(BatteryStatus());
154     return;
155   }
156
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());
162 }
163
164 class BatteryStatusObserver {
165  public:
166   explicit BatteryStatusObserver(const BatteryCallback& callback)
167       : callback_(callback) {}
168
169   ~BatteryStatusObserver() { DCHECK(!notifier_run_loop_source_); }
170
171   void Start() {
172     if (notifier_run_loop_source_)
173       return;
174
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;
183     }
184
185     CallOnBatteryStatusChanged(static_cast<void*>(&callback_));
186     CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
187                        kCFRunLoopDefaultMode);
188     UpdateNumberBatteriesHistogram(GetInternalBatteriesStates().size());
189   }
190
191   void Stop() {
192     if (!notifier_run_loop_source_)
193       return;
194
195     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
196                           kCFRunLoopDefaultMode);
197     notifier_run_loop_source_.reset();
198   }
199
200  private:
201   static void CallOnBatteryStatusChanged(void* callback) {
202     OnBatteryStatusChanged(*static_cast<BatteryCallback*>(callback));
203   }
204
205   BatteryCallback callback_;
206   base::ScopedCFTypeRef<CFRunLoopSourceRef> notifier_run_loop_source_;
207
208   DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver);
209 };
210
211 class BatteryStatusManagerMac : public BatteryStatusManager {
212  public:
213   explicit BatteryStatusManagerMac(const BatteryCallback& callback)
214       : notifier_(new BatteryStatusObserver(callback)) {}
215
216   ~BatteryStatusManagerMac() override { notifier_->Stop(); }
217
218   // BatteryStatusManager:
219   bool StartListeningBatteryChange() override {
220     notifier_->Start();
221     return true;
222   }
223
224   void StopListeningBatteryChange() override {
225     notifier_->Stop();
226   }
227
228  private:
229   scoped_ptr<BatteryStatusObserver> notifier_;
230
231   DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerMac);
232 };
233
234 } // end namespace
235
236 // static
237 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
238     const BatteryStatusService::BatteryUpdateCallback& callback) {
239   return scoped_ptr<BatteryStatusManager>(
240       new BatteryStatusManagerMac(callback));
241 }
242
243 }  // namespace device