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