| 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 "device/battery/battery_status_manager_linux.h" | |
| 6 | |
| 7 #include "base/macros.h" | |
| 8 #include "base/metrics/histogram.h" | |
| 9 #include "base/threading/thread.h" | |
| 10 #include "base/values.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/property.h" | |
| 16 #include "dbus/values_util.h" | |
| 17 #include "device/battery/battery_status_manager.h" | |
| 18 | |
| 19 namespace device { | |
| 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"; | |
| 27 const char kUPowerEnumerateDevices[] = "EnumerateDevices"; | |
| 28 const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier"; | |
| 29 | |
| 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, | |
| 42 }; | |
| 43 | |
| 44 typedef std::vector<dbus::ObjectPath> PathsVector; | |
| 45 | |
| 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; | |
| 51 } | |
| 52 | |
| 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; | |
| 58 } | |
| 59 | |
| 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); | |
| 66 | |
| 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); | |
| 77 } | |
| 78 } | |
| 79 return scoped_ptr<base::DictionaryValue>(); | |
| 80 } | |
| 81 | |
| 82 scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) { | |
| 83 scoped_ptr<PathsVector> paths(new PathsVector()); | |
| 84 if (!proxy) | |
| 85 return paths.Pass(); | |
| 86 | |
| 87 dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices); | |
| 88 scoped_ptr<dbus::Response> response( | |
| 89 proxy->CallMethodAndBlock(&method_call, | |
| 90 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
| 91 | |
| 92 if (response) { | |
| 93 dbus::MessageReader reader(response.get()); | |
| 94 reader.PopArrayOfObjectPaths(paths.get()); | |
| 95 } | |
| 96 return paths.Pass();; | |
| 97 } | |
| 98 | |
| 99 void UpdateNumberBatteriesHistogram(int count) { | |
| 100 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 101 "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6); | |
| 102 } | |
| 103 | |
| 104 // Class that represents a dedicated thread which communicates with DBus to | |
| 105 // obtain battery information and receives battery change notifications. | |
| 106 class BatteryStatusNotificationThread : public base::Thread { | |
| 107 public: | |
| 108 BatteryStatusNotificationThread( | |
| 109 const BatteryStatusService::BatteryUpdateCallback& callback) | |
| 110 : base::Thread(kBatteryNotifierThreadName), | |
| 111 callback_(callback), | |
| 112 battery_proxy_(NULL) {} | |
| 113 | |
| 114 virtual ~BatteryStatusNotificationThread() { | |
| 115 // Make sure to shutdown the dbus connection if it is still open in the very | |
| 116 // end. It needs to happen on the BatteryStatusNotificationThread. | |
| 117 message_loop()->PostTask( | |
| 118 FROM_HERE, | |
| 119 base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection, | |
| 120 base::Unretained(this))); | |
| 121 | |
| 122 // Drain the message queue of the BatteryStatusNotificationThread and stop. | |
| 123 Stop(); | |
| 124 } | |
| 125 | |
| 126 void StartListening() { | |
| 127 DCHECK(OnWatcherThread()); | |
| 128 | |
| 129 if (system_bus_.get()) | |
| 130 return; | |
| 131 | |
| 132 InitDBus(); | |
| 133 dbus::ObjectProxy* power_proxy = | |
| 134 system_bus_->GetObjectProxy(kUPowerServiceName, | |
| 135 dbus::ObjectPath(kUPowerPath)); | |
| 136 scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy); | |
| 137 int num_batteries = 0; | |
| 138 | |
| 139 for (size_t i = 0; i < device_paths->size(); ++i) { | |
| 140 const dbus::ObjectPath& device_path = device_paths->at(i); | |
| 141 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( | |
| 142 kUPowerServiceName, device_path); | |
| 143 scoped_ptr<base::DictionaryValue> dictionary = | |
| 144 GetPropertiesAsDictionary(device_proxy); | |
| 145 | |
| 146 if (!dictionary) | |
| 147 continue; | |
| 148 | |
| 149 bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false); | |
| 150 uint32 type = static_cast<uint32>( | |
| 151 GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN)); | |
| 152 | |
| 153 if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) { | |
| 154 system_bus_->RemoveObjectProxy(kUPowerServiceName, | |
| 155 device_path, | |
| 156 base::Bind(&base::DoNothing)); | |
| 157 continue; | |
| 158 } | |
| 159 | |
| 160 if (battery_proxy_) { | |
| 161 // TODO(timvolodine): add support for multiple batteries. Currently we | |
| 162 // only collect information from the first battery we encounter | |
| 163 // (crbug.com/400780). | |
| 164 LOG(WARNING) << "multiple batteries found, " | |
| 165 << "using status data of the first battery only."; | |
| 166 } else { | |
| 167 battery_proxy_ = device_proxy; | |
| 168 } | |
| 169 num_batteries++; | |
| 170 } | |
| 171 | |
| 172 UpdateNumberBatteriesHistogram(num_batteries); | |
| 173 | |
| 174 if (!battery_proxy_) { | |
| 175 callback_.Run(BatteryStatus()); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 battery_proxy_->ConnectToSignal( | |
| 180 kUPowerDeviceName, | |
| 181 kUPowerDeviceSignalChanged, | |
| 182 base::Bind(&BatteryStatusNotificationThread::BatteryChanged, | |
| 183 base::Unretained(this)), | |
| 184 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, | |
| 185 base::Unretained(this))); | |
| 186 } | |
| 187 | |
| 188 void StopListening() { | |
| 189 DCHECK(OnWatcherThread()); | |
| 190 ShutdownDBusConnection(); | |
| 191 } | |
| 192 | |
| 193 private: | |
| 194 bool OnWatcherThread() { | |
| 195 return task_runner()->BelongsToCurrentThread(); | |
| 196 } | |
| 197 | |
| 198 void InitDBus() { | |
| 199 DCHECK(OnWatcherThread()); | |
| 200 | |
| 201 dbus::Bus::Options options; | |
| 202 options.bus_type = dbus::Bus::SYSTEM; | |
| 203 options.connection_type = dbus::Bus::PRIVATE; | |
| 204 system_bus_ = new dbus::Bus(options); | |
| 205 } | |
| 206 | |
| 207 void ShutdownDBusConnection() { | |
| 208 DCHECK(OnWatcherThread()); | |
| 209 | |
| 210 if (!system_bus_.get()) | |
| 211 return; | |
| 212 | |
| 213 // Shutdown DBus connection later because there may be pending tasks on | |
| 214 // this thread. | |
| 215 message_loop()->PostTask(FROM_HERE, | |
| 216 base::Bind(&dbus::Bus::ShutdownAndBlock, | |
| 217 system_bus_)); | |
| 218 system_bus_ = NULL; | |
| 219 battery_proxy_ = NULL; | |
| 220 } | |
| 221 | |
| 222 void OnSignalConnected(const std::string& interface_name, | |
| 223 const std::string& signal_name, | |
| 224 bool success) { | |
| 225 DCHECK(OnWatcherThread()); | |
| 226 | |
| 227 if (interface_name != kUPowerDeviceName || | |
| 228 signal_name != kUPowerDeviceSignalChanged) { | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 if (!system_bus_.get()) | |
| 233 return; | |
| 234 | |
| 235 if (success) { | |
| 236 BatteryChanged(NULL); | |
| 237 } else { | |
| 238 // Failed to register for "Changed" signal, execute callback with the | |
| 239 // default values. | |
| 240 callback_.Run(BatteryStatus()); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 void BatteryChanged(dbus::Signal* signal /* unsused */) { | |
| 245 DCHECK(OnWatcherThread()); | |
| 246 | |
| 247 if (!system_bus_.get()) | |
| 248 return; | |
| 249 | |
| 250 scoped_ptr<base::DictionaryValue> dictionary = | |
| 251 GetPropertiesAsDictionary(battery_proxy_); | |
| 252 if (dictionary) | |
| 253 callback_.Run(ComputeWebBatteryStatus(*dictionary)); | |
| 254 else | |
| 255 callback_.Run(BatteryStatus()); | |
| 256 } | |
| 257 | |
| 258 BatteryStatusService::BatteryUpdateCallback callback_; | |
| 259 scoped_refptr<dbus::Bus> system_bus_; | |
| 260 dbus::ObjectProxy* battery_proxy_; // owned by the bus | |
| 261 | |
| 262 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); | |
| 263 }; | |
| 264 | |
| 265 // Creates a notification thread and delegates Start/Stop calls to it. | |
| 266 class BatteryStatusManagerLinux : public BatteryStatusManager { | |
| 267 public: | |
| 268 explicit BatteryStatusManagerLinux( | |
| 269 const BatteryStatusService::BatteryUpdateCallback& callback) | |
| 270 : callback_(callback) {} | |
| 271 | |
| 272 virtual ~BatteryStatusManagerLinux() {} | |
| 273 | |
| 274 private: | |
| 275 // BatteryStatusManager: | |
| 276 virtual bool StartListeningBatteryChange() override { | |
| 277 return false; | |
| 278 | |
| 279 notifier_thread_->message_loop()->PostTask( | |
| 280 FROM_HERE, | |
| 281 base::Bind(&BatteryStatusNotificationThread::StartListening, | |
| 282 base::Unretained(notifier_thread_.get()))); | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 virtual void StopListeningBatteryChange() override { | |
| 287 if (!notifier_thread_) | |
| 288 return; | |
| 289 | |
| 290 notifier_thread_->message_loop()->PostTask( | |
| 291 FROM_HERE, | |
| 292 base::Bind(&BatteryStatusNotificationThread::StopListening, | |
| 293 base::Unretained(notifier_thread_.get()))); | |
| 294 } | |
| 295 | |
| 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; | |
| 301 | |
| 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; | |
| 309 } | |
| 310 return true; | |
| 311 } | |
| 312 | |
| 313 BatteryStatusService::BatteryUpdateCallback callback_; | |
| 314 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_; | |
| 315 | |
| 316 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux); | |
| 317 }; | |
| 318 | |
| 319 } // namespace | |
| 320 | |
| 321 BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) { | |
| 322 BatteryStatus status; | |
| 323 if (!dictionary.HasKey("State")) | |
| 324 return status; | |
| 325 | |
| 326 uint32 state = static_cast<uint32>( | |
| 327 GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN)); | |
| 328 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING && | |
| 329 state != UPOWER_DEVICE_STATE_EMPTY; | |
| 330 double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100); | |
| 331 // Convert percentage to a value between 0 and 1 with 2 digits of precision. | |
| 332 // This is to bring it in line with other platforms like Mac and Android where | |
| 333 // we report level with 1% granularity. It also serves the purpose of reducing | |
| 334 // the possibility of fingerprinting and triggers less level change events on | |
| 335 // the blink side. | |
| 336 // TODO(timvolodine): consider moving this rounding to the blink side. | |
| 337 status.level = round(percentage) / 100.f; | |
| 338 | |
| 339 switch (state) { | |
| 340 case UPOWER_DEVICE_STATE_CHARGING : { | |
| 341 double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0); | |
| 342 status.charging_time = | |
| 343 (time_to_full > 0) ? time_to_full | |
| 344 : std::numeric_limits<double>::infinity(); | |
| 345 break; | |
| 346 } | |
| 347 case UPOWER_DEVICE_STATE_DISCHARGING : { | |
| 348 double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0); | |
| 349 // Set dischargingTime if it's available. Otherwise leave the default | |
| 350 // value which is +infinity. | |
| 351 if (time_to_empty > 0) | |
| 352 status.discharging_time = time_to_empty; | |
| 353 status.charging_time = std::numeric_limits<double>::infinity(); | |
| 354 break; | |
| 355 } | |
| 356 case UPOWER_DEVICE_STATE_FULL : { | |
| 357 break; | |
| 358 } | |
| 359 default: { | |
| 360 status.charging_time = std::numeric_limits<double>::infinity(); | |
| 361 } | |
| 362 } | |
| 363 return status; | |
| 364 } | |
| 365 | |
| 366 // static | |
| 367 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
| 368 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
| 369 return scoped_ptr<BatteryStatusManager>( | |
| 370 new BatteryStatusManagerLinux(callback)); | |
| 371 } | |
| 372 | |
| 373 } // namespace device | |
| OLD | NEW |