| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2010, 2011 The Chromium OS 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 "src/device_impl.h" |
| 6 |
| 7 #include <glog/logging.h> |
| 8 |
| 9 #include "src/byte_counter.h" |
| 10 #include "src/service.h" |
| 11 |
| 12 namespace cashew { |
| 13 |
| 14 // Flimflam Device D-Bus identifiers |
| 15 static const char *kFlimflamDeviceName = "org.chromium.flimflam"; |
| 16 |
| 17 // Flimflam Device property names |
| 18 static const char *kFlimflamDeviceCarrierProperty = "Cellular.Carrier"; |
| 19 static const char *kFlimflamDeviceInterfaceProperty = "Interface"; |
| 20 static const char *kFlimflamDeviceTypeProperty = "Type"; |
| 21 |
| 22 // Flimflam Device on-the-wire Type values |
| 23 static const char *kFlimflamDeviceTypeEthernet = "ethernet"; |
| 24 static const char *kFlimflamDeviceTypeWifi = "wifi"; |
| 25 static const char *kFlimflamDeviceTypeWimax = "wimax"; |
| 26 static const char *kFlimflamDeviceTypeBluetooth = "bluetooth"; |
| 27 static const char *kFlimflamDeviceTypeGPS = "gps"; |
| 28 static const char *kFlimflamDeviceTypeCellular = "cellular"; |
| 29 // TODO(vlaviano): what does vendor look like? |
| 30 static const char *kFlimflamDeviceTypeVendor = ""; |
| 31 |
| 32 // GetDeviceProperties retry interval |
| 33 static const guint kSecondsPerMinute = 60; |
| 34 static const guint kGetDevicePropertiesIntervalSeconds = 1 * kSecondsPerMinute; |
| 35 |
| 36 DeviceImpl::DeviceImpl(Service * const parent, |
| 37 DBus::Connection& connection, // NOLINT |
| 38 const DBus::Path& path) |
| 39 : DBus::ObjectProxy(connection, path, kFlimflamDeviceName), |
| 40 parent_(CHECK_NOTNULL(parent)), path_(path), type_(kTypeUnknown), |
| 41 get_properties_source_id_(0), retrying_get_properties_(false), |
| 42 byte_counter_(NULL), byte_counter_running_(false) { |
| 43 // schedule a GetProperties() call to our Flimflam service path to init state |
| 44 // we'll keep trying periodically until we succeed |
| 45 // we'll subsequently update this state by monitoring PropertyChanged signals |
| 46 get_properties_source_id_ = |
| 47 g_idle_add(StaticGetDevicePropertiesCallback, this); |
| 48 if (get_properties_source_id_ == 0) { |
| 49 LOG(ERROR) << path_ << ": ctor: g_idle_add failed"; |
| 50 } |
| 51 property_changed_handler_.delegate(this); |
| 52 } |
| 53 |
| 54 DeviceImpl::~DeviceImpl() { |
| 55 StopByteCounter(); |
| 56 if (get_properties_source_id_ != 0 && |
| 57 !g_source_remove(get_properties_source_id_)) { |
| 58 DLOG(WARNING) << path_ << ": dtor: g_source_remove failed"; |
| 59 } |
| 60 } |
| 61 |
| 62 const DBus::Path& DeviceImpl::GetPath() const { |
| 63 return path_; |
| 64 } |
| 65 |
| 66 Device::Type DeviceImpl::GetType() const { |
| 67 return type_; |
| 68 } |
| 69 |
| 70 const std::string& DeviceImpl::GetCarrier() const { |
| 71 return carrier_; |
| 72 } |
| 73 |
| 74 const std::string& DeviceImpl::GetInterface() const { |
| 75 return interface_; |
| 76 } |
| 77 |
| 78 // Flimflam Device D-Bus Proxy methods |
| 79 |
| 80 void DeviceImpl::PropertyChanged(const std::string& property_name, |
| 81 const DBus::Variant& new_value) { |
| 82 DLOG(INFO) << path_ << ": PropertyChanged: property_name = " << property_name; |
| 83 // Queue a tuple representing this signal for later processing from the glib |
| 84 // main loop. We do this to avoid libdbus-c++ deadlocks that can occur when |
| 85 // sending a dbus message from within a dbus callback like this one. |
| 86 PropertyChangedSignal signal(property_name, new_value); |
| 87 property_changed_handler_.EnqueueSignal(signal); |
| 88 } |
| 89 |
| 90 // PropertyChangedDelegate methods |
| 91 |
| 92 void DeviceImpl::OnPropertyChanged(const PropertyChangedHandler *handler, |
| 93 const std::string& property_name, |
| 94 const DBus::Variant& new_value) { |
| 95 DCHECK(handler == &property_changed_handler_); |
| 96 DLOG(INFO) << path_ << ": OnPropertyChanged: property_name = " |
| 97 << property_name; |
| 98 if (property_name == kFlimflamDeviceCarrierProperty) { |
| 99 OnCarrierUpdate(new_value.reader().get_string()); |
| 100 } else if (property_name == kFlimflamDeviceInterfaceProperty) { |
| 101 OnInterfaceUpdate(new_value.reader().get_string()); |
| 102 } else if (property_name == kFlimflamDeviceTypeProperty) { |
| 103 OnTypeUpdate(new_value.reader().get_string()); |
| 104 } else { |
| 105 // we don't care about this property |
| 106 } |
| 107 } |
| 108 |
| 109 // Service methods |
| 110 |
| 111 bool DeviceImpl::StartByteCounter() { |
| 112 if (byte_counter_running_) { |
| 113 DLOG(WARNING) << path_ << ": StartByteCounter: counter already running"; |
| 114 return false; |
| 115 } |
| 116 byte_counter_running_ = true; |
| 117 if (interface_.empty()) { |
| 118 // if we don't yet know our interface name, we report success but hold off |
| 119 // creating a byte counter object; we'll do so later in OnInterfaceUpdate |
| 120 DLOG(INFO) << path_ |
| 121 << ": StartByteCounter: no interface name, delaying counter creation"; |
| 122 return true; |
| 123 } |
| 124 if (!CreateByteCounter()) { |
| 125 DLOG(WARNING) << path_ |
| 126 << ": StartByteCounter: couldn't create byte counter for " << interface_; |
| 127 byte_counter_running_ = false; |
| 128 return false; |
| 129 } |
| 130 DLOG(INFO) << path_ << ": StartByteCounter: created byte counter for " |
| 131 << interface_; |
| 132 return true; |
| 133 } |
| 134 |
| 135 void DeviceImpl::StopByteCounter() { |
| 136 if (byte_counter_running_) { |
| 137 DeleteByteCounter(); |
| 138 byte_counter_running_ = false; |
| 139 } |
| 140 DCHECK(byte_counter_ == NULL); |
| 141 } |
| 142 |
| 143 bool DeviceImpl::ByteCounterRunning() const { |
| 144 return byte_counter_running_; |
| 145 } |
| 146 |
| 147 // ByteCounterDelegate methods |
| 148 |
| 149 void DeviceImpl::OnByteCounterUpdate(const ByteCounter *counter, |
| 150 uint64 rx_bytes, uint64 tx_bytes) { |
| 151 DCHECK(byte_counter_running_); |
| 152 DCHECK(byte_counter_ != NULL); |
| 153 DCHECK(counter == byte_counter_); |
| 154 DLOG(INFO) << path_ << ": OnByteCounterUpdate: rx_bytes = " << rx_bytes |
| 155 << ", tx_bytes = " << tx_bytes; |
| 156 parent_->OnByteCounterUpdate(rx_bytes, tx_bytes); |
| 157 } |
| 158 |
| 159 // Private methods |
| 160 |
| 161 Device::Type DeviceImpl::TypeFromString(const std::string& type) const { |
| 162 if (type == kFlimflamDeviceTypeEthernet) { |
| 163 return kTypeEthernet; |
| 164 } |
| 165 if (type == kFlimflamDeviceTypeWifi) { |
| 166 return kTypeWifi; |
| 167 } |
| 168 if (type == kFlimflamDeviceTypeWimax) { |
| 169 return kTypeWimax; |
| 170 } |
| 171 if (type == kFlimflamDeviceTypeBluetooth) { |
| 172 return kTypeBluetooth; |
| 173 } |
| 174 if (type == kFlimflamDeviceTypeGPS) { |
| 175 return kTypeGPS; |
| 176 } |
| 177 if (type == kFlimflamDeviceTypeCellular) { |
| 178 return kTypeCellular; |
| 179 } |
| 180 if (type == kFlimflamDeviceTypeVendor) { |
| 181 return kTypeVendor; |
| 182 } |
| 183 return kTypeUnknown; |
| 184 } |
| 185 |
| 186 void DeviceImpl::OnCarrierUpdate(const std::string& carrier) { |
| 187 DLOG(INFO) << path_ << ": OnCarrierUpdate: carrier = " << carrier; |
| 188 // only propagate this to parent Service if there's been a change |
| 189 // we expect to see only one such change (from "Unknown") |
| 190 if (carrier_ == carrier) { |
| 191 return; |
| 192 } |
| 193 if (!carrier_.empty()) { |
| 194 LOG(WARNING) << path_ << ": OnCarrierUpdate: carrier change from " |
| 195 << carrier_ << " to " << carrier << "!"; |
| 196 } |
| 197 carrier_ = carrier; |
| 198 parent_->OnCarrierUpdate(carrier); |
| 199 } |
| 200 |
| 201 void DeviceImpl::OnInterfaceUpdate(const std::string& interface) { |
| 202 DLOG(INFO) << path_ << ": OnInterfaceUpdate: interface = " << interface; |
| 203 if (interface_ == interface) { |
| 204 return; |
| 205 } |
| 206 if (!interface_.empty()) { |
| 207 LOG(WARNING) << path_ << ": OnInterfaceUpdate: interface change from " |
| 208 << interface_ << " to " << interface << "!"; |
| 209 } |
| 210 const std::string old_interface = interface_; |
| 211 interface_ = interface; |
| 212 if (!byte_counter_running_) { |
| 213 return; |
| 214 } |
| 215 // get rid of byte counter for old interface |
| 216 StopByteCounter(); |
| 217 // if we don't have new interface info, we can't make a new byte counter yet |
| 218 if (interface_.empty()) { |
| 219 return; |
| 220 } |
| 221 // create byte counter for new interface |
| 222 // TODO(vlaviano): creating new counter on interface change may confuse our |
| 223 // parent service. |
| 224 if (!StartByteCounter()) { |
| 225 LOG(ERROR) << path_ |
| 226 << ": OnInterfaceUpdate: could not create byte counter for " |
| 227 << interface_; |
| 228 return; |
| 229 } |
| 230 } |
| 231 |
| 232 void DeviceImpl::OnTypeUpdate(const std::string& type) { |
| 233 DLOG(INFO) << path_ << ": OnTypeUpdate: type = " << type; |
| 234 type_ = TypeFromString(type); |
| 235 if (type_ != kTypeCellular) { |
| 236 LOG(WARNING) << path_ << ": OnTypeUpdate: type is not cellular!"; |
| 237 } |
| 238 } |
| 239 |
| 240 // static |
| 241 gboolean DeviceImpl::StaticGetDevicePropertiesCallback(gpointer data) { |
| 242 DeviceImpl *device = reinterpret_cast<DeviceImpl*>(data); |
| 243 CHECK_NOTNULL(device); |
| 244 DCHECK_NE(device->get_properties_source_id_, 0); |
| 245 if (!device->GetDeviceProperties()) { |
| 246 // call failed, so try again later |
| 247 DLOG(WARNING) << device->GetPath() |
| 248 << ": StaticGetDevicePropertiesCallback: " |
| 249 << "GetDeviceProperties failed, will retry in " |
| 250 << kGetDevicePropertiesIntervalSeconds << " secs"; |
| 251 // if we've arrived here from our first g_idle_add call, set up a timer |
| 252 if (!device->retrying_get_properties_) { |
| 253 guint source_id = |
| 254 g_timeout_add_seconds(kGetDevicePropertiesIntervalSeconds, |
| 255 StaticGetDevicePropertiesCallback, device); |
| 256 device->get_properties_source_id_ = source_id; |
| 257 if (source_id == 0) { |
| 258 LOG(ERROR) << device->GetPath() |
| 259 << ": StaticGetDevicePropertiesCallback: " |
| 260 << "g_timeout_add_seconds failed"; |
| 261 return FALSE; // get rid of old glib source and give up |
| 262 } |
| 263 device->retrying_get_properties_ = true; |
| 264 // get rid of our old glib source, but our new timer will call us |
| 265 return FALSE; |
| 266 } |
| 267 // otherwise, our timer is already set up |
| 268 // return TRUE to indicate that we want to be called again later |
| 269 return TRUE; |
| 270 } |
| 271 device->get_properties_source_id_ = 0; |
| 272 device->retrying_get_properties_ = false; |
| 273 return FALSE; // we succeeded, so don't repeat |
| 274 } |
| 275 |
| 276 bool DeviceImpl::GetDeviceProperties() { |
| 277 DLOG(INFO) << path_ << ": GetDeviceProperties"; |
| 278 PropertyMap properties; |
| 279 // dbus-c++ throws exceptions |
| 280 // invoke the "Existing Non-conformant Code" clause of the style guide and |
| 281 // isolate the rest of the system from this |
| 282 try { |
| 283 // TODO(vlaviano): make this call asynchronous |
| 284 properties = GetProperties(); |
| 285 } catch (DBus::Error& error) { // NOLINT |
| 286 LOG(WARNING) << path_ |
| 287 << ": GetDeviceProperties: GetProperties() -> Exception: " |
| 288 << error.name() << ": " << error.message(); |
| 289 return false; |
| 290 } catch (...) { // NOLINT |
| 291 LOG(WARNING) << path_ |
| 292 << ": GetDeviceProperties: GetProperties() -> Exception"; |
| 293 return false; |
| 294 } |
| 295 DLOG(INFO) << "Received " << properties.size() << " properties"; |
| 296 |
| 297 // grab the properties in which we're interested |
| 298 PropertyMap::const_iterator it; |
| 299 it = properties.find(kFlimflamDeviceInterfaceProperty); |
| 300 if (it != properties.end()) { |
| 301 const DBus::Variant& value = static_cast<DBus::Variant>(it->second); |
| 302 OnInterfaceUpdate(value.reader().get_string()); |
| 303 } else { |
| 304 DLOG(WARNING) << path_ << ": GetDeviceProperties: no Interface property"; |
| 305 } |
| 306 it = properties.find(kFlimflamDeviceTypeProperty); |
| 307 if (it != properties.end()) { |
| 308 const DBus::Variant& value = static_cast<DBus::Variant>(it->second); |
| 309 OnTypeUpdate(value.reader().get_string()); |
| 310 } else { |
| 311 DLOG(WARNING) << path_ << ": GetDeviceProperties: no Type property"; |
| 312 } |
| 313 // don't expect to find Cellular.* properties if we're not a cellular device |
| 314 if (type_ != kTypeCellular) { |
| 315 return true; |
| 316 } |
| 317 it = properties.find(kFlimflamDeviceCarrierProperty); |
| 318 if (it != properties.end()) { |
| 319 const DBus::Variant& value = static_cast<DBus::Variant>(it->second); |
| 320 OnCarrierUpdate(value.reader().get_string()); |
| 321 } else { |
| 322 DLOG(WARNING) << path_ |
| 323 << ": GetDeviceProperties: no Cellular.Carrier property"; |
| 324 } |
| 325 return true; |
| 326 } |
| 327 |
| 328 bool DeviceImpl::CreateByteCounter() { |
| 329 if (byte_counter_ != NULL) { |
| 330 DLOG(WARNING) << path_ << ": CreateByteCounter: counter already exists"; |
| 331 return false; |
| 332 } |
| 333 if (interface_.empty()) { |
| 334 DLOG(WARNING) << path_ << ": CreateByteCounter: interface name unknown"; |
| 335 return false; |
| 336 } |
| 337 byte_counter_ = ByteCounter::NewByteCounter(interface_); |
| 338 if (byte_counter_ == NULL) { |
| 339 LOG(ERROR) << path_ |
| 340 << ": CreateByteCounter: could not create byte counter for " |
| 341 << interface_; |
| 342 return false; |
| 343 } |
| 344 byte_counter_->SetDelegate(this); |
| 345 return true; |
| 346 } |
| 347 |
| 348 void DeviceImpl::DeleteByteCounter() { |
| 349 DLOG(INFO) << path_ << ": DeleteByteCounter"; |
| 350 if (byte_counter_ != NULL) { |
| 351 delete byte_counter_; |
| 352 byte_counter_ = NULL; |
| 353 } |
| 354 } |
| 355 |
| 356 } // namespace cashew |
| OLD | NEW |