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

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: rebase 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
« no previous file with comments | « no previous file | content/content_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.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 #include "third_party/cros_system_api/dbus/service_constants.h"
mlamouri (slow - plz ping) 2014/08/01 17:14:14 I think you should remove that #include. Isn't it
timvolodine 2014/08/06 16:37:42 Done.
18
19 namespace content {
20
21 namespace {
22
23 const char kUPowerServiceName[] = "org.freedesktop.UPower";
24 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
25 const char kUPowerPath[] = "/org/freedesktop/UPower";
26 const char kUPowerDeviceSignalChanged[] = "Changed";
mlamouri (slow - plz ping) 2014/08/01 17:14:13 I think you can use "DeviceChanged" instead of "Ch
timvolodine 2014/08/06 16:37:42 UPower.Device only has the Changed() signal, in li
mlamouri (slow - plz ping) 2014/08/07 16:17:04 My bad, I misread. I didn't see that you were regi
27 const char kBatteryWatcherThreadName[] = "BatteryStatusWatcher";
28
29 // intended for unit32, int64, uint64, double.
30 template<typename T>
31 T GetProperty(const base::DictionaryValue& dictionary,
32 const std::string& property_name,
33 T default_value) {
34 double value;
35 return dictionary.GetDouble(property_name, &value) ? value : default_value;
36 }
37
38 // bool
39 template<>
40 bool GetProperty(const base::DictionaryValue& dictionary,
41 const std::string& property_name,
42 bool default_value) {
43 bool value;
44 return dictionary.GetBoolean(property_name, &value) ? value : default_value;
45 }
46
47 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
48 dbus::ObjectProxy* proxy) {
49 dbus::MethodCall method_call(dbus::kDBusPropertiesInterface,
50 dbus::kDBusPropertiesGetAll);
51 dbus::MessageWriter builder(&method_call);
52 builder.AppendString(kUPowerDeviceName);
53
54 scoped_ptr<dbus::Response> response(
55 proxy->CallMethodAndBlock(&method_call,
56 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
57 if (response) {
58 dbus::MessageReader reader(response.get());
59 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
60 base::DictionaryValue* dictionary_value = NULL;
61 if (value && value->GetAsDictionary(&dictionary_value)) {
62 ignore_result(value.release());
63 return scoped_ptr<base::DictionaryValue>(dictionary_value);
64 }
65 }
66 return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue());
67 }
68
69 void GetPowerSourcesPaths(dbus::ObjectProxy* proxy,
70 std::vector<dbus::ObjectPath>& paths) {
71 if (!proxy)
72 return;
73
74 dbus::MethodCall method_call(kUPowerServiceName, "EnumerateDevices");
75 scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
76 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
77
78 if (response) {
79 dbus::MessageReader reader(response.get());
80 if (reader.PopArrayOfObjectPaths(&paths))
81 return;
82 }
83 paths.clear();
84 }
85
86 class BatteryStatusNotificationThread : public base::Thread {
87 public:
88 BatteryStatusNotificationThread(
89 const BatteryStatusService::BatteryUpdateCallback& callback)
90 : base::Thread(kBatteryWatcherThreadName),
91 callback_(callback),
92 battery_proxy_(0) {}
93
94 virtual ~BatteryStatusNotificationThread() {
95 if (system_bus_)
96 ShutdownDBus();
97 Stop();
98 }
99
100 void StartListening() {
101 DCHECK(OnWatcherThread());
102
103 if (system_bus_)
104 return;
105
106 InitDBus();
107 dbus::ObjectProxy* power_proxy =
108 system_bus_->GetObjectProxy(kUPowerServiceName,
109 dbus::ObjectPath(kUPowerPath));
110 std::vector<dbus::ObjectPath> device_paths;
111 GetPowerSourcesPaths(power_proxy, device_paths);
112
113 for (size_t i = 0; i < device_paths.size(); ++i) {
114 const dbus::ObjectPath& device_path = device_paths[i];
115 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
116 kUPowerServiceName, device_path);
117 scoped_ptr<base::DictionaryValue> dictionary =
118 GetPropertiesAsDictionary(device_proxy);
119
120 bool is_present = GetProperty<bool>(*dictionary, "IsPresent", false);
121 uint32 type = GetProperty<uint32>(*dictionary, "Type", 0);
122
123 if (is_present && type == 2 /* battery */) {
124 if (battery_proxy_) {
125 // TODO(timvolodine): add support for multiple batteries. Currently we
Michael van Ouwerkerk 2014/08/01 15:24:39 Please file a bug for this. It would be nice to ha
timvolodine 2014/08/06 16:37:42 Done.
126 // only collect information from the first battery we encounter.
127 LOG(WARNING) << "multiple batteries found, "
128 << "using status data of the first battery only.";
129 } else {
130 battery_proxy_ = device_proxy;
131 }
132 }
133 }
134
135 if (!battery_proxy_) {
136 callback_.Run(blink::WebBatteryStatus());
137 return;
138 }
139
140 battery_proxy_->ConnectToSignal(
141 kUPowerDeviceName,
142 kUPowerDeviceSignalChanged,
143 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
144 base::Unretained(this)),
145 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
146 base::Unretained(this)));
147 }
148
149 void StopListening() {
150 DCHECK(OnWatcherThread());
151
152 if (!system_bus_)
153 return;
154
155 ShutdownDBus();
mlamouri (slow - plz ping) 2014/08/01 17:14:13 nit: could you rename this ShutdownDBusConnection(
timvolodine 2014/08/06 16:37:42 Done.
156 }
157
158 private:
159
160 bool OnWatcherThread() {
161 return std::string(base::PlatformThread::GetName()).compare(
162 kBatteryWatcherThreadName) == 0;
163 }
164
165 void InitDBus() {
166 DCHECK(OnWatcherThread());
167
168 dbus::Bus::Options options;
169 options.bus_type = dbus::Bus::SYSTEM;
170 options.connection_type = dbus::Bus::PRIVATE;
171 system_bus_ = new dbus::Bus(options);
172 }
173
174 void ShutdownDBus() {
175 // Shutdown DBus connection later because there may be pending tasks on
176 // this thread.
177 message_loop()->PostTask(FROM_HERE,
178 base::Bind(&dbus::Bus::ShutdownAndBlock,
179 system_bus_));
180 system_bus_ = 0;
181 battery_proxy_ = 0;
182 }
183
184 void OnSignalConnected(const std::string& interface_name,
185 const std::string& signal_name,
186 bool success) {
187 DCHECK(OnWatcherThread());
188
189 if (interface_name.compare(kUPowerDeviceName) != 0 ||
190 signal_name.compare(kUPowerDeviceSignalChanged) != 0) {
191 return;
192 }
193
194 if (!system_bus_)
195 return;
196
197 if (success) {
198 BatteryChanged(0);
199 } else {
200 // Failed to register for "Changed" signal, execute callback with the
201 // default values.
202 callback_.Run(blink::WebBatteryStatus());
203 }
204 }
205
206 void BatteryChanged(dbus::Signal* signal) {
Michael van Ouwerkerk 2014/08/01 15:24:39 Maybe a unit test would be appropriate here. I thi
Michael van Ouwerkerk 2014/08/01 15:24:39 The signal param initially confused me. How about
timvolodine 2014/08/06 16:37:42 Done.
timvolodine 2014/08/06 16:37:42 I've extracted a method for testing (ComputeWebBat
207 DCHECK(OnWatcherThread());
208
209 blink::WebBatteryStatus status;
210 scoped_ptr<base::DictionaryValue> dictionary =
211 GetPropertiesAsDictionary(battery_proxy_);
212
213 uint32 state = GetProperty<uint32>(*dictionary, "State", -1);
214 status.charging = state != 2; // true if not discharging
mlamouri (slow - plz ping) 2014/08/01 17:14:14 Could you have an enum that mirror the possible va
timvolodine 2014/08/06 16:37:42 Done.
215 double percentage = GetProperty<double>(*dictionary, "Percentage", 1);
216 // Convert percentage to a value between 0 and 1 with 3 digits of precision.
217 status.level = round(percentage * 10) / 1000.f;
mlamouri (slow - plz ping) 2014/08/01 17:14:13 Why not *0.01.f or /10.f?
timvolodine 2014/08/06 16:37:42 dbus reports the value with lost of fractional dig
218
219 switch (state) {
220 case 1 : { // charging
221 int64 time_to_full = GetProperty<int64>(*dictionary, "TimeToFull", 0);
222 status.chargingTime =
mlamouri (slow - plz ping) 2014/08/01 17:14:13 Are we using the right default value in Blink if t
timvolodine 2014/08/06 16:37:42 here we explicitly set it to either the time or +i
223 (time_to_full > 0) ? time_to_full
224 : std::numeric_limits<double>::infinity();
225 break;
226 }
227 case 2 : { // discharging
228 int64 time_to_empty = GetProperty<int64>(*dictionary, "TimeToEmpty", 0);
229 // Set dischargingTime if it's available. Otherwise leave the default
230 // value which is +infinity.
231 if (time_to_empty > 0)
232 status.dischargingTime = time_to_empty;
mlamouri (slow - plz ping) 2014/08/01 17:14:13 ditto
timvolodine 2014/08/06 16:37:43 dischargingTime is +inf by default so we are fine
233 status.chargingTime = std::numeric_limits<double>::infinity();
234 break;
235 }
236 case 4 : { // fully charged
237 break;
238 }
239 default: {
240 status.chargingTime = std::numeric_limits<double>::infinity();
241 }
242 }
243
244 callback_.Run(status);
245 }
246
247 BatteryStatusService::BatteryUpdateCallback callback_;
248 scoped_refptr<dbus::Bus> system_bus_;
249 dbus::ObjectProxy* battery_proxy_; // owned by dbus
250
251 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
252 };
253
254 class BatteryStatusManagerLinux : public BatteryStatusManager {
255 public:
256 explicit BatteryStatusManagerLinux(
257 const BatteryStatusService::BatteryUpdateCallback& callback)
258 : callback_(callback) {}
259
260 virtual ~BatteryStatusManagerLinux() {}
261
262 private:
263 // BatteryStatusManager:
264 virtual bool StartListeningBatteryChange() OVERRIDE {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
266 if (!notifier_thread_) {
267 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
268 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
269 if (!notifier_thread_->StartWithOptions(thread_options)) {
270 notifier_thread_.reset();
271 return false;
mlamouri (slow - plz ping) 2014/08/01 17:14:14 That's not really something we expect to happen, r
timvolodine 2014/08/06 16:37:42 right, added LOG(ERROR).
272 }
273 }
274
275 notifier_thread_->message_loop()->PostTask(
276 FROM_HERE,
277 base::Bind(&BatteryStatusNotificationThread::StartListening,
278 base::Unretained(notifier_thread_.get())));
279 return true;
280 }
281
282 virtual void StopListeningBatteryChange() OVERRIDE {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
284
285 notifier_thread_->message_loop()->PostTask(
286 FROM_HERE,
287 base::Bind(&BatteryStatusNotificationThread::StopListening,
288 base::Unretained(notifier_thread_.get())));
289 }
290
291 BatteryStatusService::BatteryUpdateCallback callback_;
292 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
293
294 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
295 };
296
297 } // namespace
298
299 // static
300 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
301 const BatteryStatusService::BatteryUpdateCallback& callback) {
302 return scoped_ptr<BatteryStatusManager>(
303 new BatteryStatusManagerLinux(callback));
304 }
305
306 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/content_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698