Remove implicit conversions from scoped_refptr to T* in content/
[chromium-blink-merge.git] / content / browser / battery_status / battery_status_manager_linux.cc
blob6539f05714180257c0670c160057c0d16eda4c5f
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.
5 #include "content/browser/battery_status/battery_status_manager_linux.h"
7 #include "base/macros.h"
8 #include "base/threading/thread.h"
9 #include "base/values.h"
10 #include "content/browser/battery_status/battery_status_manager.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "dbus/bus.h"
13 #include "dbus/message.h"
14 #include "dbus/object_path.h"
15 #include "dbus/object_proxy.h"
16 #include "dbus/property.h"
17 #include "dbus/values_util.h"
19 namespace content {
21 namespace {
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";
27 const char kUPowerEnumerateDevices[] = "EnumerateDevices";
28 const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
30 // UPowerDeviceType reflects the possible UPower.Device.Type values,
31 // see upower.freedesktop.org/docs/Device.html#Device:Type.
32 enum UPowerDeviceType {
33 UPOWER_DEVICE_TYPE_UNKNOWN = 0,
34 UPOWER_DEVICE_TYPE_LINE_POWER = 1,
35 UPOWER_DEVICE_TYPE_BATTERY = 2,
36 UPOWER_DEVICE_TYPE_UPS = 3,
37 UPOWER_DEVICE_TYPE_MONITOR = 4,
38 UPOWER_DEVICE_TYPE_MOUSE = 5,
39 UPOWER_DEVICE_TYPE_KEYBOARD = 6,
40 UPOWER_DEVICE_TYPE_PDA = 7,
41 UPOWER_DEVICE_TYPE_PHONE = 8,
44 typedef std::vector<dbus::ObjectPath> PathsVector;
46 double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
47 const std::string& property_name,
48 double default_value) {
49 double value = default_value;
50 return dictionary.GetDouble(property_name, &value) ? value : default_value;
53 bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
54 const std::string& property_name,
55 bool default_value) {
56 bool value = default_value;
57 return dictionary.GetBoolean(property_name, &value) ? value : default_value;
60 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
61 dbus::ObjectProxy* proxy) {
62 dbus::MethodCall method_call(dbus::kPropertiesInterface,
63 dbus::kPropertiesGetAll);
64 dbus::MessageWriter builder(&method_call);
65 builder.AppendString(kUPowerDeviceName);
67 scoped_ptr<dbus::Response> response(
68 proxy->CallMethodAndBlock(&method_call,
69 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
70 if (response) {
71 dbus::MessageReader reader(response.get());
72 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
73 base::DictionaryValue* dictionary_value = NULL;
74 if (value && value->GetAsDictionary(&dictionary_value)) {
75 ignore_result(value.release());
76 return scoped_ptr<base::DictionaryValue>(dictionary_value);
79 return scoped_ptr<base::DictionaryValue>();
82 scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
83 scoped_ptr<PathsVector> paths(new PathsVector());
84 if (!proxy)
85 return paths.Pass();
87 dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
88 scoped_ptr<dbus::Response> response(
89 proxy->CallMethodAndBlock(&method_call,
90 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
92 if (response) {
93 dbus::MessageReader reader(response.get());
94 reader.PopArrayOfObjectPaths(paths.get());
96 return paths.Pass();;
99 // Class that represents a dedicated thread which communicates with DBus to
100 // obtain battery information and receives battery change notifications.
101 class BatteryStatusNotificationThread : public base::Thread {
102 public:
103 BatteryStatusNotificationThread(
104 const BatteryStatusService::BatteryUpdateCallback& callback)
105 : base::Thread(kBatteryNotifierThreadName),
106 callback_(callback),
107 battery_proxy_(NULL) {}
109 virtual ~BatteryStatusNotificationThread() {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
112 // Make sure to shutdown the dbus connection if it is still open in the very
113 // end. It needs to happen on the BatteryStatusNotificationThread.
114 message_loop()->PostTask(
115 FROM_HERE,
116 base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
117 base::Unretained(this)));
119 // Drain the message queue of the BatteryStatusNotificationThread and stop.
120 Stop();
123 void StartListening() {
124 DCHECK(OnWatcherThread());
126 if (system_bus_.get())
127 return;
129 InitDBus();
130 dbus::ObjectProxy* power_proxy =
131 system_bus_->GetObjectProxy(kUPowerServiceName,
132 dbus::ObjectPath(kUPowerPath));
133 scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
135 for (size_t i = 0; i < device_paths->size(); ++i) {
136 const dbus::ObjectPath& device_path = device_paths->at(i);
137 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
138 kUPowerServiceName, device_path);
139 scoped_ptr<base::DictionaryValue> dictionary =
140 GetPropertiesAsDictionary(device_proxy);
142 if (!dictionary)
143 continue;
145 bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
146 uint32 type = static_cast<uint32>(
147 GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
149 if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
150 system_bus_->RemoveObjectProxy(kUPowerServiceName,
151 device_path,
152 base::Bind(&base::DoNothing));
153 continue;
156 if (battery_proxy_) {
157 // TODO(timvolodine): add support for multiple batteries. Currently we
158 // only collect information from the first battery we encounter
159 // (crbug.com/400780).
160 // TODO(timvolodine): add UMA logging for this case.
161 LOG(WARNING) << "multiple batteries found, "
162 << "using status data of the first battery only.";
163 } else {
164 battery_proxy_ = device_proxy;
168 if (!battery_proxy_) {
169 callback_.Run(blink::WebBatteryStatus());
170 return;
173 battery_proxy_->ConnectToSignal(
174 kUPowerDeviceName,
175 kUPowerDeviceSignalChanged,
176 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
177 base::Unretained(this)),
178 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
179 base::Unretained(this)));
182 void StopListening() {
183 DCHECK(OnWatcherThread());
184 ShutdownDBusConnection();
187 private:
188 bool OnWatcherThread() {
189 return task_runner()->BelongsToCurrentThread();
192 void InitDBus() {
193 DCHECK(OnWatcherThread());
195 dbus::Bus::Options options;
196 options.bus_type = dbus::Bus::SYSTEM;
197 options.connection_type = dbus::Bus::PRIVATE;
198 system_bus_ = new dbus::Bus(options);
201 void ShutdownDBusConnection() {
202 DCHECK(OnWatcherThread());
204 if (!system_bus_.get())
205 return;
207 // Shutdown DBus connection later because there may be pending tasks on
208 // this thread.
209 message_loop()->PostTask(FROM_HERE,
210 base::Bind(&dbus::Bus::ShutdownAndBlock,
211 system_bus_));
212 system_bus_ = NULL;
213 battery_proxy_ = NULL;
216 void OnSignalConnected(const std::string& interface_name,
217 const std::string& signal_name,
218 bool success) {
219 DCHECK(OnWatcherThread());
221 if (interface_name != kUPowerDeviceName ||
222 signal_name != kUPowerDeviceSignalChanged) {
223 return;
226 if (!system_bus_.get())
227 return;
229 if (success) {
230 BatteryChanged(NULL);
231 } else {
232 // Failed to register for "Changed" signal, execute callback with the
233 // default values.
234 callback_.Run(blink::WebBatteryStatus());
238 void BatteryChanged(dbus::Signal* signal /* unsused */) {
239 DCHECK(OnWatcherThread());
241 if (!system_bus_.get())
242 return;
244 scoped_ptr<base::DictionaryValue> dictionary =
245 GetPropertiesAsDictionary(battery_proxy_);
246 if (dictionary)
247 callback_.Run(ComputeWebBatteryStatus(*dictionary));
248 else
249 callback_.Run(blink::WebBatteryStatus());
252 BatteryStatusService::BatteryUpdateCallback callback_;
253 scoped_refptr<dbus::Bus> system_bus_;
254 dbus::ObjectProxy* battery_proxy_; // owned by the bus
256 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
259 // Runs on IO thread and creates a notification thread and delegates Start/Stop
260 // calls to it.
261 class BatteryStatusManagerLinux : public BatteryStatusManager {
262 public:
263 explicit BatteryStatusManagerLinux(
264 const BatteryStatusService::BatteryUpdateCallback& callback)
265 : callback_(callback) {}
267 virtual ~BatteryStatusManagerLinux() {}
269 private:
270 // BatteryStatusManager:
271 virtual bool StartListeningBatteryChange() OVERRIDE {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
274 if (!StartNotifierThreadIfNecessary())
275 return false;
277 notifier_thread_->message_loop()->PostTask(
278 FROM_HERE,
279 base::Bind(&BatteryStatusNotificationThread::StartListening,
280 base::Unretained(notifier_thread_.get())));
281 return true;
284 virtual void StopListeningBatteryChange() OVERRIDE {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
287 if (!notifier_thread_)
288 return;
290 notifier_thread_->message_loop()->PostTask(
291 FROM_HERE,
292 base::Bind(&BatteryStatusNotificationThread::StopListening,
293 base::Unretained(notifier_thread_.get())));
296 // Starts the notifier thread if not already started and returns true on
297 // success.
298 bool StartNotifierThreadIfNecessary() {
299 if (notifier_thread_)
300 return true;
302 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
303 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
304 if (!notifier_thread_->StartWithOptions(thread_options)) {
305 notifier_thread_.reset();
306 LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
307 << " thread";
308 return false;
310 return true;
313 BatteryStatusService::BatteryUpdateCallback callback_;
314 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
316 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
319 } // namespace
321 blink::WebBatteryStatus ComputeWebBatteryStatus(
322 const base::DictionaryValue& dictionary) {
323 blink::WebBatteryStatus status;
324 if (!dictionary.HasKey("State"))
325 return status;
327 uint32 state = static_cast<uint32>(
328 GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
329 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
330 state != UPOWER_DEVICE_STATE_EMPTY;
331 double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
332 // Convert percentage to a value between 0 and 1 with 2 digits of precision.
333 // This is to bring it in line with other platforms like Mac and Android where
334 // we report level with 1% granularity. It also serves the purpose of reducing
335 // the possibility of fingerprinting and triggers less level change events on
336 // the blink side.
337 // TODO(timvolodine): consider moving this rounding to the blink side.
338 status.level = round(percentage) / 100.f;
340 switch (state) {
341 case UPOWER_DEVICE_STATE_CHARGING : {
342 double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
343 status.chargingTime =
344 (time_to_full > 0) ? time_to_full
345 : std::numeric_limits<double>::infinity();
346 break;
348 case UPOWER_DEVICE_STATE_DISCHARGING : {
349 double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
350 // Set dischargingTime if it's available. Otherwise leave the default
351 // value which is +infinity.
352 if (time_to_empty > 0)
353 status.dischargingTime = time_to_empty;
354 status.chargingTime = std::numeric_limits<double>::infinity();
355 break;
357 case UPOWER_DEVICE_STATE_FULL : {
358 break;
360 default: {
361 status.chargingTime = std::numeric_limits<double>::infinity();
364 return status;
367 // static
368 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
369 const BatteryStatusService::BatteryUpdateCallback& callback) {
370 return scoped_ptr<BatteryStatusManager>(
371 new BatteryStatusManagerLinux(callback));
374 } // namespace content