Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Side by Side Diff: content/browser/battery_status/battery_status_manager_linux.cc

Issue 436683002: Battery Status API: implementation for Linux. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_linux.h"
6
7 #include "base/macros.h"
8 #include "base/threading/thread.h"
9 #include "base/values.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "dbus/bus.h"
12 #include "dbus/message.h"
13 #include "dbus/object_path.h"
14 #include "dbus/object_proxy.h"
15 #include "dbus/values_util.h"
16 #include "third_party/WebKit/public/platform/WebBatteryStatus.h"
17
18 namespace content {
19
20 namespace {
21
22 const char kUPowerServiceName[] = "org.freedesktop.UPower";
23 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
24 const char kUPowerPath[] = "/org/freedesktop/UPower";
25 const char kUPowerDeviceSignalChanged[] = "Changed";
26 const char kBatteryWatcherThreadName[] = "BatteryStatusWatcher";
27 const char kDBusPropertiesInterface[] = "org.freedesktop.DBus.Properties";
28 const char kDBusPropertiesGetAll[] = "GetAll";
29
30 // intended for unit32, int64, uint64, double.
Michael van Ouwerkerk 2014/08/08 14:54:59 s/unit32/uint32/
timvolodine 2014/08/08 18:46:45 Done.
31 template<typename T>
32 T GetProperty(const base::DictionaryValue& dictionary,
33 const std::string& property_name,
34 T default_value) {
35 double value;
36 return dictionary.GetDouble(property_name, &value) ? value : default_value;
stevenjb 2014/08/08 16:18:16 static_cast<T>(value)
timvolodine 2014/08/08 18:46:45 Done.
37 }
38
39 // bool
40 template<>
41 bool GetProperty(const base::DictionaryValue& dictionary,
42 const std::string& property_name,
43 bool default_value) {
44 bool value;
45 return dictionary.GetBoolean(property_name, &value) ? value : default_value;
46 }
47
48 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
49 dbus::ObjectProxy* proxy) {
50 dbus::MethodCall method_call(kDBusPropertiesInterface,
51 kDBusPropertiesGetAll);
52 dbus::MessageWriter builder(&method_call);
53 builder.AppendString(kUPowerDeviceName);
54
55 scoped_ptr<dbus::Response> response(
56 proxy->CallMethodAndBlock(&method_call,
57 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
58 if (response) {
stevenjb 2014/08/08 16:18:16 We should probably return NULL on failure to read
timvolodine 2014/08/08 18:46:45 the idea here is to return an empty dictionary, wh
59 dbus::MessageReader reader(response.get());
60 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
61 base::DictionaryValue* dictionary_value = NULL;
62 if (value && value->GetAsDictionary(&dictionary_value)) {
63 ignore_result(value.release());
64 return scoped_ptr<base::DictionaryValue>(dictionary_value);
65 }
66 }
67 return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue());
68 }
69
70 void GetPowerSourcesPaths(dbus::ObjectProxy* proxy,
71 std::vector<dbus::ObjectPath>& paths) {
72 if (!proxy)
73 return;
74
75 dbus::MethodCall method_call(kUPowerServiceName, "EnumerateDevices");
76 scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
77 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
78
79 if (response) {
80 dbus::MessageReader reader(response.get());
81 if (reader.PopArrayOfObjectPaths(&paths))
82 return;
83 }
84 paths.clear();
85 }
86
87 class BatteryStatusNotificationThread : public base::Thread {
88 public:
89 BatteryStatusNotificationThread(
90 const BatteryStatusService::BatteryUpdateCallback& callback)
91 : base::Thread(kBatteryWatcherThreadName),
92 callback_(callback),
93 battery_proxy_(0) {}
94
95 virtual ~BatteryStatusNotificationThread() {
96 if (system_bus_)
97 ShutdownDBusConnection();
98 Stop();
99 }
100
101 void StartListening() {
102 DCHECK(OnWatcherThread());
103
104 if (system_bus_)
105 return;
106
107 InitDBus();
108 dbus::ObjectProxy* power_proxy =
109 system_bus_->GetObjectProxy(kUPowerServiceName,
110 dbus::ObjectPath(kUPowerPath));
111 std::vector<dbus::ObjectPath> device_paths;
112 GetPowerSourcesPaths(power_proxy, device_paths);
113
114 for (size_t i = 0; i < device_paths.size(); ++i) {
115 const dbus::ObjectPath& device_path = device_paths[i];
116 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
117 kUPowerServiceName, device_path);
118 scoped_ptr<base::DictionaryValue> dictionary =
119 GetPropertiesAsDictionary(device_proxy);
120
121 bool is_present = GetProperty<bool>(*dictionary, "IsPresent", false);
122 uint32 type = GetProperty<uint32>(*dictionary, "Type", 0);
123
124 if (is_present && type == 2 /* battery */) {
mlamouri (slow - plz ping) 2014/08/07 16:17:04 Could you add an enum for that too? Sorry for not
stevenjb 2014/08/08 16:18:16 nit: if (!is_present || type != TYPE_BATTERY) cont
timvolodine 2014/08/08 18:46:45 Done.
timvolodine 2014/08/08 18:46:45 Done.
125 if (battery_proxy_) {
126 // TODO(timvolodine): add support for multiple batteries. Currently we
127 // only collect information from the first battery we encounter
128 // (crbug.com/400780).
129 // TODO(timvolodine): add UMA logging for this case.
130 LOG(WARNING) << "multiple batteries found, "
131 << "using status data of the first battery only.";
132 } else {
133 battery_proxy_ = device_proxy;
134 }
135 }
136 }
137
138 if (!battery_proxy_) {
139 callback_.Run(blink::WebBatteryStatus());
140 return;
141 }
142
143 battery_proxy_->ConnectToSignal(
144 kUPowerDeviceName,
145 kUPowerDeviceSignalChanged,
146 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
147 base::Unretained(this)),
148 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
149 base::Unretained(this)));
150 }
151
152 void StopListening() {
153 DCHECK(OnWatcherThread());
154
155 if (!system_bus_)
156 return;
157
158 ShutdownDBusConnection();
159 }
160
161 private:
162
163 bool OnWatcherThread() {
164 return std::string(base::PlatformThread::GetName()).compare(
165 kBatteryWatcherThreadName) == 0;
166 }
167
168 void InitDBus() {
169 DCHECK(OnWatcherThread());
170
171 dbus::Bus::Options options;
172 options.bus_type = dbus::Bus::SYSTEM;
173 options.connection_type = dbus::Bus::PRIVATE;
174 system_bus_ = new dbus::Bus(options);
175 }
176
177 void ShutdownDBusConnection() {
178 // Shutdown DBus connection later because there may be pending tasks on
179 // this thread.
180 message_loop()->PostTask(FROM_HERE,
181 base::Bind(&dbus::Bus::ShutdownAndBlock,
182 system_bus_));
183 system_bus_ = 0;
184 battery_proxy_ = 0;
185 }
186
187 void OnSignalConnected(const std::string& interface_name,
188 const std::string& signal_name,
189 bool success) {
190 DCHECK(OnWatcherThread());
191
192 if (interface_name.compare(kUPowerDeviceName) != 0 ||
193 signal_name.compare(kUPowerDeviceSignalChanged) != 0) {
194 return;
195 }
196
197 if (!system_bus_)
198 return;
199
200 if (success) {
201 BatteryChanged(0);
202 } else {
203 // Failed to register for "Changed" signal, execute callback with the
204 // default values.
205 callback_.Run(blink::WebBatteryStatus());
206 }
207 }
208
209 void BatteryChanged(dbus::Signal* signal /* unsused */) {
210 DCHECK(OnWatcherThread());
211 blink::WebBatteryStatus status;
212 scoped_ptr<base::DictionaryValue> dictionary =
213 GetPropertiesAsDictionary(battery_proxy_);
214 ComputeWebBatteryStatus(*dictionary, status);
215 callback_.Run(status);
mlamouri (slow - plz ping) 2014/08/07 16:17:04 This block needs some empty lines. It's quite dens
timvolodine 2014/08/08 18:46:45 Done.
216 }
217
218 BatteryStatusService::BatteryUpdateCallback callback_;
219 scoped_refptr<dbus::Bus> system_bus_;
220 dbus::ObjectProxy* battery_proxy_; // owned by dbus
221
222 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
223 };
224
225 class BatteryStatusManagerLinux : public BatteryStatusManager {
226 public:
227 explicit BatteryStatusManagerLinux(
228 const BatteryStatusService::BatteryUpdateCallback& callback)
229 : callback_(callback) {}
230
231 virtual ~BatteryStatusManagerLinux() {}
232
233 private:
234 // BatteryStatusManager:
235 virtual bool StartListeningBatteryChange() OVERRIDE {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237 if (!notifier_thread_) {
238 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
239 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
240 if (!notifier_thread_->StartWithOptions(thread_options)) {
241 notifier_thread_.reset();
242 LOG(ERROR) << "Could not start the " << kBatteryWatcherThreadName
243 << "thread";
244 return false;
245 }
246 }
stevenjb 2014/08/08 16:18:16 This bit should probably be moved to a helper meth
timvolodine 2014/08/08 18:46:45 Done.
247
248 notifier_thread_->message_loop()->PostTask(
249 FROM_HERE,
250 base::Bind(&BatteryStatusNotificationThread::StartListening,
251 base::Unretained(notifier_thread_.get())));
252 return true;
253 }
254
255 virtual void StopListeningBatteryChange() OVERRIDE {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
257
258 notifier_thread_->message_loop()->PostTask(
259 FROM_HERE,
260 base::Bind(&BatteryStatusNotificationThread::StopListening,
261 base::Unretained(notifier_thread_.get())));
262 }
263
264 BatteryStatusService::BatteryUpdateCallback callback_;
265 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
266
267 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
268 };
269
270 } // namespace
271
272 // Computes and sets the WebBatteryStatus values where possible based on the
273 // passed dictionary.
274 void ComputeWebBatteryStatus(const base::DictionaryValue& dictionary,
275 blink::WebBatteryStatus& status) {
276 if (!dictionary.HasKey("State"))
277 return;
278 uint32 state = GetProperty<uint32>(dictionary, "State",
279 UPOWER_DEVICE_STATE_UNKNOWN);
280 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING;
281 double percentage = GetProperty<double>(dictionary, "Percentage", 100);
282 // Convert percentage to a value between 0 and 1 with 3 digits of precision.
283 status.level = round(percentage * 10) / 1000.f;
mlamouri (slow - plz ping) 2014/08/07 16:17:04 Could you add a test with "Percentage" = 13.37 and
timvolodine 2014/08/08 18:46:45 Done.
284
285 switch (state) {
286 case UPOWER_DEVICE_STATE_CHARGING : {
287 int64 time_to_full = GetProperty<int64>(dictionary, "TimeToFull", 0);
288 status.chargingTime =
289 (time_to_full > 0) ? time_to_full
290 : std::numeric_limits<double>::infinity();
291 break;
292 }
293 case UPOWER_DEVICE_STATE_DISCHARGING : {
294 int64 time_to_empty = GetProperty<int64>(dictionary, "TimeToEmpty", 0);
295 // Set dischargingTime if it's available. Otherwise leave the default
296 // value which is +infinity.
297 if (time_to_empty > 0)
298 status.dischargingTime = time_to_empty;
299 status.chargingTime = std::numeric_limits<double>::infinity();
300 break;
301 }
302 case UPOWER_DEVICE_STATE_FULL : {
303 break;
304 }
305 default: {
306 status.chargingTime = std::numeric_limits<double>::infinity();
307 }
308 }
309 }
310
311 // static
312 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
313 const BatteryStatusService::BatteryUpdateCallback& callback) {
314 return scoped_ptr<BatteryStatusManager>(
315 new BatteryStatusManagerLinux(callback));
316 }
317
318 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698