Cashew: track cellular service state. BUG=chromium-os:7190 TEST=libcros/monitor_network and manual testing, observing cashew logs Change-Id: I60a175f72dc665e12ab0eaa1c87054ad4c2dfe8b Review URL: http://codereview.chromium.org/3576011
diff --git a/Makefile.am b/Makefile.am index 8d91b48..2a94be8 100644 --- a/Makefile.am +++ b/Makefile.am
@@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -SUBDIRS = introspection src +SUBDIRS = extrospection introspection src EXTRA_DIST = \ cashew.conf \
diff --git a/configure.ac b/configure.ac index 579fc96..6c2c32c 100644 --- a/configure.ac +++ b/configure.ac
@@ -11,6 +11,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile + extrospection/Makefile introspection/Makefile src/Makefile ])
diff --git a/extrospection/Makefile.am b/extrospection/Makefile.am new file mode 100644 index 0000000..7a976f2 --- /dev/null +++ b/extrospection/Makefile.am
@@ -0,0 +1,8 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +EXTRA_DIST = \ + flimflam-device.xml \ + flimflam-manager.xml \ + flimflam-service.xml
diff --git a/extrospection/flimflam-device.xml b/extrospection/flimflam-device.xml new file mode 100644 index 0000000..0a2ab3b --- /dev/null +++ b/extrospection/flimflam-device.xml
@@ -0,0 +1,29 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + Introspection dump generated manually by poking flimflam with dbus-send + TODO(vlaviano): should be generated automatically during flimflam build + and installed somewhere where cashew and other clients can consume it. +--> + +<node> + <interface name="org.chromium.flimflam.Device"> + <method name="GetProperties"> + <arg type="a{sv}" direction="out"/> + </method> + <method name="SetProperty"> + <arg type="s" direction="in"/> + <arg type="v" direction="in"/> + </method> + <method name="ProposeScan"/> + <method name="AddIPConfig"> + <arg type="s" direction="in"/> + <arg type="o" direction="out"/> + </method> + <signal name="PropertyChanged"> + <arg type="s"/> + <arg type="v"/> + </signal> + </interface> +</node>
diff --git a/extrospection/flimflam-manager.xml b/extrospection/flimflam-manager.xml new file mode 100644 index 0000000..a7e21cd --- /dev/null +++ b/extrospection/flimflam-manager.xml
@@ -0,0 +1,86 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + Introspection dump generated manually by poking flimflam with dbus-send + TODO(vlaviano): should be generated automatically during flimflam build + and installed somewhere where cashew and other clients can consume it. +--> + +<node> + <interface name="org.chromium.flimflam.Manager"> + <method name="GetProperties"> + <arg type="a{sv}" direction="out"/> + </method> + <method name="SetProperty"> + <arg type="s" direction="in"/> + <arg type="v" direction="in"/> + </method> + <method name="GetState"> + <arg type="s" direction="out"/> + </method> + <method name="CreateProfile"> + <arg type="s" direction="in"/> + <arg type="o" direction="out"/> + </method> + <method name="RemoveProfile"> + <arg type="o" direction="in"/> + </method> + <method name="RequestScan"> + <arg type="s" direction="in"/> + </method> + <method name="EnableTechnology"> + <arg type="s" direction="in"/> + </method> + <method name="DisableTechnology"> + <arg type="s" direction="in"/> + </method> + <method name="GetService"> + <arg type="a{sv}" direction="in"/> + <arg type="o" direction="out"/> + </method> + <method name="GetWifiService"> + <arg type="a{sv}" direction="in"/> + <arg type="o" direction="out"/> + </method> + <method name="ConfigureWifiService"> + <arg type="a{sv}" direction="in"/> + </method> + <method name="RegisterAgent"> + <arg type="o" direction="in"/> + </method> + <method name="UnregisterAgent"> + <arg type="o" direction="in"/> + </method> + <method name="GetDebugTags"> + <arg type="s" direction="out"/> + </method> + <method name="SetDebugTags"> + <arg type="s" direction="in"/> + </method> + <method name="ListDebugTags"> + <arg type="s" direction="out"/> + </method> + <method name="GetDebugMask"> + <arg type="u" direction="out"/> + </method> + <method name="SetDebugMask"> + <arg type="u" direction="in"/> + </method> + <method name="GetServiceOrder"> + <arg type="s" direction="out"/> + </method> + <method name="SetServiceOrder"> + <arg type="s" direction="in"/> + </method> + <signal name="PropertyChanged"> + <arg type="s"/> + <arg type="v"/> + </signal> + <signal name="StateChanged"> + <arg type="s"/> + </signal> + </interface> + <node name="device"/> + <node name="profile"/> +</node>
diff --git a/extrospection/flimflam-service.xml b/extrospection/flimflam-service.xml new file mode 100644 index 0000000..6dd75ef --- /dev/null +++ b/extrospection/flimflam-service.xml
@@ -0,0 +1,39 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + Introspection dump generated manually by poking flimflam with dbus-send + TODO(vlaviano): should be generated automatically during flimflam build + and installed somewhere where cashew and other clients can consume it. +--> + +<node> + <interface name="org.chromium.flimflam.Service"> + <method name="GetProperties"> + <arg type="a{sv}" direction="out"/> + </method> + <method name="SetProperty"> + <arg type="s" direction="in"/> + <arg type="v" direction="in"/> + </method> + <method name="ClearProperty"> + <arg type="s" direction="in"/> + </method> + <method name="Connect"/> + <method name="Disconnect"/> + <method name="Remove"/> + <method name="MoveBefore"> + <arg type="o" direction="in"/> + </method> + <method name="MoveAfter"> + <arg type="o" direction="in"/> + </method> + <method name="ActivateCellularModem"> + <arg type="s" direction="in"/> + </method> + <signal name="PropertyChanged"> + <arg type="s"/> + <arg type="v"/> + </signal> + </interface> +</node>
diff --git a/introspection/cashew.xml b/introspection/cashew.xml index 312fd0b..f546a28 100644 --- a/introspection/cashew.xml +++ b/introspection/cashew.xml
@@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" ?> -<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> +<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> <interface name="org.chromium.Cashew"> <tp:simple-type name="Timestamp" type="x"> @@ -87,6 +87,11 @@ A list of plan structs describing the data plans for |service|. </tp:docstring> </arg> + <tp:possible-errors> + <tp:error name="org.chromium.Cashew.Error.ServiceUnknown"> + The service path does not correspond to a known service. + </tp:error> + </tp:possible-errors> </method> <method name="RequestDataPlansUpdate"> @@ -98,6 +103,11 @@ The dbus path of the cellular service for which caller wants info. </tp:docstring> </arg> + <tp:possible-errors> + <tp:error name="org.chromium.Cashew.Error.ServiceUnknown"> + The service path does not correspond to a known service. + </tp:error> + </tp:possible-errors> </method> <method name="IsAlive">
diff --git a/src/Makefile.am b/src/Makefile.am index 38d7580..26dd136 100644 --- a/src/Makefile.am +++ b/src/Makefile.am
@@ -7,6 +7,14 @@ cashewd_SOURCES = \ cashew_server.h \ cashew_server.cc \ + data_plan.h \ + data_plan.cc \ + device.h \ + device.cc \ + service.h \ + service.cc \ + service_manager.h \ + service_manager.cc \ main.cc # D-Bus server stub generation @@ -14,22 +22,38 @@ cashew_server_glue.h: $(top_srcdir)/introspection/cashew.xml $(DBUSXX_XML2CPP) $^ --adaptor=$@ +# D-Bus client stub generation +flimflam_device_client_glue.h: $(top_srcdir)/extrospection/flimflam-device.xml + $(DBUSXX_XML2CPP) $^ --proxy=$@ +flimflam_manager_client_glue.h: $(top_srcdir)/extrospection/flimflam-manager.xml + $(DBUSXX_XML2CPP) $^ --proxy=$@ +flimflam_service_client_glue.h: $(top_srcdir)/extrospection/flimflam-service.xml + $(DBUSXX_XML2CPP) $^ --proxy=$@ + BUILT_SOURCES = \ - cashew_server_glue.h + cashew_server_glue.h \ + flimflam_device_client_glue.h \ + flimflam_manager_client_glue.h \ + flimflam_service_client_glue.h CLEANFILES = $(BUILT_SOURCES) -# manually defining these instead of getting them via autoconf, because gflags -# has no pkg-config file +# manually defining info for libs with no pkg-config file + +LIBBASE_CFLAGS = +LIBBASE_LIBS = -lbase + GFLAGS_CFLAGS = GFLAGS_LIBS = -lgflags cashewd_CPPFLAGS = -Wall -Werror \ + $(LIBBASE_CFLAGS) \ @DBUS_CPP_CFLAGS@ \ $(GFLAGS_CFLAGS) \ @GLOG_CFLAGS@ cashewd_LDADD = \ + $(LIBBASE_LIBS) \ @DBUS_CPP_LIBS@ \ $(GFLAGS_LIBS) \ @GLOG_LIBS@
diff --git a/src/cashew_server.cc b/src/cashew_server.cc index a5c0aa4..54c5281 100644 --- a/src/cashew_server.cc +++ b/src/cashew_server.cc
@@ -11,36 +11,52 @@ const char* CashewServer::kServiceName = "org.chromium.Cashew"; const char* CashewServer::kServicePath = "/org/chromium/Cashew"; -CashewServer::CashewServer(DBus::Connection& connection) // NOLINT - : DBus::ObjectAdaptor(connection, kServicePath) { +// Cashew D-Bus errors +const char* kCashewErrorServiceUnknown = + "org.chromium.Cashew.Error.ServiceUnknown"; + +CashewServer::CashewServer(DBus::Connection& connection, // NOLINT + const ServiceManager& service_manager) + : DBus::ObjectAdaptor(connection, kServicePath), + service_manager_(service_manager) { +} + +CashewServer::~CashewServer() { } // Cashew D-Bus API methods -DataPlanList CashewServer::GetDataPlans(const std::string& service, - DBus::Error& error) { // NOLINT - DLOG(INFO) << "GetDataPlans: service = " << service; - // TODO(vlaviano): log and return error if we don't like service arg - - // For now, we return hardcoded data. - // TODO(vlaviano): return real data. - DataPlan hardcoded_plan; - GetHardcodedDataPlan(&hardcoded_plan); - DataPlanList data_plans; - data_plans.push_back(hardcoded_plan); - return data_plans; +DBusDataPlanList CashewServer::GetDataPlans(const std::string& service_path, + DBus::Error& error) { // NOLINT + DLOG(INFO) << "GetDataPlans: service = " << service_path; + DBusDataPlanList dbus_data_plans; + const Service *service = service_manager_.GetService(service_path); + if (service == NULL) { + DLOG(WARNING) << "GetDataPlans: unknown service: " << service_path; + error.set(kCashewErrorServiceUnknown, service_path.c_str()); + DCHECK(dbus_data_plans.empty()); + return dbus_data_plans; + } + dbus_data_plans = service->GetDBusDataPlans(); + return dbus_data_plans; } -void CashewServer::RequestDataPlansUpdate(const std::string& service, +void CashewServer::RequestDataPlansUpdate(const std::string& service_path, DBus::Error& error) { // NOLINT - DLOG(INFO) << "RequestDataPlansUpdate: service = " << service; - // TODO(vlaviano): log and return error if we don't like service arg + DLOG(INFO) << "RequestDataPlansUpdate: service = " << service_path; + const Service *service = service_manager_.GetService(service_path); + if (service == NULL) { + DLOG(WARNING) << "RequestDataPlansUpdate: unknown service: " + << service_path; + error.set(kCashewErrorServiceUnknown, service_path.c_str()); + return; + } // trigger a DataPlansUpdate signal // TODO(vlaviano): rate-limiting? // TODO(vlaviano): if cached data isn't recent, ask carrier API for fresh // data and wait for reply before sending signal. - EmitDataPlansUpdateSignal(service); + EmitDataPlansUpdateSignal(*service); } bool CashewServer::IsAlive(DBus::Error& error) { // NOLINT @@ -50,39 +66,12 @@ // Private methods -void CashewServer::EmitDataPlansUpdateSignal(const std::string& service) { - DLOG(INFO) << "EmitDataPlansUpdateSignal: service = " << service; - // TODO(vlaviano): log error and return if we don't like service arg - - // For now, we return hardcoded data. - // TODO(vlaviano): return real data. - DataPlan hardcoded_plan; - GetHardcodedDataPlan(&hardcoded_plan); - DataPlanList data_plans; - data_plans.push_back(hardcoded_plan); +void CashewServer::EmitDataPlansUpdateSignal(const Service& service) { + DLOG(INFO) << "EmitDataPlansUpdateSignal: service = " << service.GetPath(); + DBusDataPlanList dbus_data_plans = service.GetDBusDataPlans(); // send out the signal - this->DataPlansUpdate(service, data_plans); -} - -void CashewServer::GetHardcodedDataPlan(DataPlan* plan) { - DCHECK(plan != NULL); - - const std::string plan_name = "test"; - const std::string plan_type = "UNLIMITED"; - int64 plan_update_time = 0; - int64 plan_start_time = time(NULL) - 12*3600; // 12 hours ago - int64 plan_end_time = time(NULL) + 12*3600; // 12 hours hence - int64 plan_data_bytes = 100*1024*1024; // 100 MB - int64 plan_data_bytes_used = 30*1024*1024; // 30 MB - - (*plan)["CellularPlanName"].writer().append_string(plan_name.c_str()); - (*plan)["CellularPlanType"].writer().append_string(plan_type.c_str()); - (*plan)["CellularPlanUpdateTime"].writer().append_int64(plan_update_time); - (*plan)["CellularPlanStart"].writer().append_int64(plan_start_time); - (*plan)["CellularPlanEnd"].writer().append_int64(plan_end_time); - (*plan)["CellularPlanDataBytes"].writer().append_int64(plan_data_bytes); - (*plan)["CellularDataBytesUsed"].writer().append_int64(plan_data_bytes_used); + DataPlansUpdate(service.GetPath(), dbus_data_plans); } } // namespace cashew
diff --git a/src/cashew_server.h b/src/cashew_server.h index 7bfc5bd..cf5a4e6 100644 --- a/src/cashew_server.h +++ b/src/cashew_server.h
@@ -13,28 +13,29 @@ #include <dbus-c++/dbus.h> // NOLINT #include "src/cashew_server_glue.h" +#include "src/data_plan.h" +#include "src/service.h" +#include "src/service_manager.h" namespace cashew { -typedef std::map<std::string, DBus::Variant> DataPlan; -typedef std::vector<DataPlan> DataPlanList; - // Implements Cashew D-Bus API class CashewServer : public org::chromium::Cashew_adaptor, public DBus::IntrospectableAdaptor, public DBus::ObjectAdaptor { public: - explicit CashewServer(DBus::Connection& connection); // NOLINT - virtual ~CashewServer() {} + CashewServer(DBus::Connection& connection, // NOLINT + const ServiceManager& service_manager); + virtual ~CashewServer(); // Cashew D-Bus API methods // Get data plan info for a cellular service - virtual DataPlanList GetDataPlans(const std::string& service, - DBus::Error& error); // NOLINT + virtual DBusDataPlanList GetDataPlans(const std::string& service_path, + DBus::Error& error); // NOLINT // Request that a DataPlansUpdate signal be sent for a cellular service - virtual void RequestDataPlansUpdate(const std::string& service, + virtual void RequestDataPlansUpdate(const std::string& service_path, DBus::Error& error); // NOLINT // Test method to verify that Cashew service is working @@ -44,11 +45,10 @@ static const char* kServicePath; private: - // Emit a DataPlansUpdate signal for a service - void EmitDataPlansUpdateSignal(const std::string& service); + const ServiceManager& service_manager_; - // Fill in data plan with hardcoded data for testing - void GetHardcodedDataPlan(DataPlan* data_plan); + // Emit a DataPlansUpdate signal for a service + void EmitDataPlansUpdateSignal(const Service& service); DISALLOW_COPY_AND_ASSIGN(CashewServer); };
diff --git a/src/data_plan.cc b/src/data_plan.cc new file mode 100644 index 0000000..5b42d9d --- /dev/null +++ b/src/data_plan.cc
@@ -0,0 +1,102 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/data_plan.h" + +#include <glog/logging.h> + +namespace cashew { + +// libcros CellularDataPlan property names +static const char* kCellularDataPlanName = "CellularPlanName"; +static const char* kCellularDataPlanType = "CellularPlanType"; +static const char* kCellularDataPlanUpdateTime = "CellularPlanUpdateTime"; +static const char* kCellularDataPlanStartTime = "CellularPlanStart"; +static const char* kCellularDataPlanEndTime = "CellularPlanEnd"; +static const char* kCellularDataPlanDataBytesMax = "CellularPlanDataBytes"; +static const char* kCellularDataPlanDataBytesUsed = "CellularDataBytesUsed"; + +// libcros CellularDataPlan type values +static const char* kCellularDataPlanTypeUnlimited = "UNLIMITED"; +static const char* kCellularDataPlanTypeMeteredFree = "METERED_BASE"; +static const char* kCellularDataPlanTypeMeteredPaid = "METERED_PAID"; + +DataPlan::DataPlan(const std::string& name, DataPlan::Type type, + base::Time update_time, base::Time start_time, + base::Time end_time, Bytes data_bytes_max, + Bytes data_bytes_used) + : name_(name), type_(type), update_time_(update_time), + start_time_(start_time), end_time_(end_time), + data_bytes_max_(data_bytes_max), data_bytes_used_(data_bytes_used) { +} + +DataPlan::~DataPlan() { +} + +const std::string& DataPlan::GetName() const { + return name_; +} + +DataPlan::Type DataPlan::GetType() const { + return type_; +} + +base::Time DataPlan::GetUpdateTime() const { + return update_time_; +} + +base::Time DataPlan::GetStartTime() const { + return start_time_; +} + +base::Time DataPlan::GetEndTime() const { + return end_time_; +} + +Bytes DataPlan::GetDataBytesMax() const { + return data_bytes_max_; +} + +Bytes DataPlan::GetDataBytesUsed() const { + return data_bytes_used_; +} + +DBusDataPlan DataPlan::ToDBusFormat() const { + DBusDataPlan plan; + // Indexing into map w/ nonexistent key causes empty DBus::Variant to be + // created and inserted. We then fill in the desired value via its writer + // DBus::MessageIterator. + plan[kCellularDataPlanName].writer().append_string(name_.c_str()); + plan[kCellularDataPlanType].writer().append_string(TypeToString(type_)); + plan[kCellularDataPlanUpdateTime].writer().append_int64( + update_time_.ToInternalValue()); + plan[kCellularDataPlanStartTime].writer().append_int64( + start_time_.ToInternalValue()); + plan[kCellularDataPlanEndTime].writer().append_int64( + end_time_.ToInternalValue()); + // omit max bytes field for unlimited plans + // can libcros/Chrome deal? if not, can set this to int64 max. + if (type_ != kTypeUnlimited) { + plan[kCellularDataPlanDataBytesMax].writer().append_int64(data_bytes_max_); + } + plan[kCellularDataPlanDataBytesUsed].writer().append_int64(data_bytes_used_); + return plan; +} + +// private methods + +const char* DataPlan::TypeToString(DataPlan::Type type) const { + switch (type) { + case kTypeUnlimited: + return kCellularDataPlanTypeUnlimited; + case kTypeMeteredFree: + return kCellularDataPlanTypeMeteredFree; + case kTypeMeteredPaid: + return kCellularDataPlanTypeMeteredPaid; + default: + DCHECK(false); + } +} + +} // namespace cashew
diff --git a/src/data_plan.h b/src/data_plan.h new file mode 100644 index 0000000..47d2b2c --- /dev/null +++ b/src/data_plan.h
@@ -0,0 +1,103 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_DATA_PLAN_H_ +#define SRC_DATA_PLAN_H_ + +#include <map> +#include <string> +#include <vector> + +#include <base/basictypes.h> // NOLINT +#include <base/time.h> // NOLINT +#include <dbus-c++/dbus.h> // NOLINT + +namespace cashew { + +class DataPlan; + +// byte count +// TODO(vlaviano): this should be a uint64, but libcros/Chrome expects this +typedef int64 Bytes; + +// List of DataPlan objects +typedef std::vector<DataPlan*> DataPlanList; + +// DataPlan object formatted for D-Bus (using DBus::Variants to wrap fields) +typedef std::map<std::string, DBus::Variant> DBusDataPlan; + +// List of DataPlan objects formatted for D-Bus +typedef std::vector<DBusDataPlan> DBusDataPlanList; + +// Represents a data plan associated with a cellular service +class DataPlan { + public: + // valid type values for this plan + typedef enum { + kTypeUnlimited = 0, + kTypeMeteredFree, + kTypeMeteredPaid, + } Type; + + DataPlan(const std::string& name, DataPlan::Type type, + base::Time update_time, base::Time start_time, + base::Time end_time, Bytes data_bytes_max, Bytes data_bytes_used); + virtual ~DataPlan(); + + // get plan name + virtual const std::string& GetName() const; + + // get plan type + virtual Type GetType() const; + + // get last update time + virtual base::Time GetUpdateTime() const; + + // get plan start time + virtual base::Time GetStartTime() const; + + // get plan end time + virtual base::Time GetEndTime() const; + + // get plan max bytes + virtual Bytes GetDataBytesMax() const; + + // get plan used bytes + virtual Bytes GetDataBytesUsed() const; + + // return a{sv} representation for D-Bus + virtual DBusDataPlan ToDBusFormat() const; + + private: + // human-readable plan name + const std::string name_; + + // plan type + const Type type_; + + // last update time (i.e., how fresh is plan data) + base::Time update_time_; + + // plan start time + const base::Time start_time_; + + // plan end time + const base::Time end_time_; + + // max bytes available during plan period + // irrelevant for plans of type kTypeUnlimited + const Bytes data_bytes_max_; + + // bytes consumed so far during plan period + Bytes data_bytes_used_; + + // convert plan type enum value to string + const char* TypeToString(Type) const; + + DISALLOW_COPY_AND_ASSIGN(DataPlan); +}; + +} // namespace cashew + +#endif // SRC_DATA_PLAN_H_
diff --git a/src/device.cc b/src/device.cc new file mode 100644 index 0000000..9f60c8e --- /dev/null +++ b/src/device.cc
@@ -0,0 +1,159 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/device.h" + +#include <glog/logging.h> + +#include "src/service.h" + +namespace cashew { + +const char* Device::kCarrierUnknown = "Unknown"; + +// Flimflam Device D-Bus identifiers +static const char* kFlimflamDeviceName = "org.chromium.flimflam"; + +// Flimflam Device property names +static const char* kFlimflamDeviceCarrierProperty = "Cellular.Carrier"; +static const char* kFlimflamDeviceTypeProperty = "Type"; + +// Flimflam Device on-the-wire Type values +static const char* kFlimflamDeviceTypeEthernet = "ethernet"; +static const char* kFlimflamDeviceTypeWifi = "wifi"; +static const char* kFlimflamDeviceTypeWimax = "wimax"; +static const char* kFlimflamDeviceTypeBluetooth = "bluetooth"; +static const char* kFlimflamDeviceTypeGPS = "gps"; +static const char* kFlimflamDeviceTypeCellular = "cellular"; +// TODO(vlaviano): what does vendor look like? +static const char* kFlimflamDeviceTypeVendor = ""; + +Device::Device(DBus::Connection& connection, // NOLINT + const DBus::Path& path) + : DBus::ObjectProxy(connection, path, kFlimflamDeviceName), + path_(path), type_(kTypeUnknown), carrier_(kCarrierUnknown) { + // init our state with a GetProperties() call to our Flimflam service path + // we'll update this state by monitoring PropertyChanged signals + GetDeviceProperties(); +} + +Device::~Device() { +} + +const DBus::Path& Device::GetPath() const { + return path_; +} + +Device::Type Device::GetType() const { + return type_; +} + +const std::string& Device::GetCarrier() const { + return carrier_; +} + +// Flimflam Device D-Bus Proxy methods +void Device::PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value) { + DLOG(INFO) << path_ << ": PropertyChanged: property_name = " << property_name; + if (property_name.compare(kFlimflamDeviceCarrierProperty) == 0) { + OnCarrierUpdate(new_value.reader().get_string()); + } else if (property_name.compare(kFlimflamDeviceTypeProperty) == 0) { + OnTypeUpdate(new_value.reader().get_string()); + } else { + // we don't care about this property + } +} + +// Private methods + +Device::Type Device::TypeFromString(const std::string& type) const { + if (type.compare(kFlimflamDeviceTypeEthernet) == 0) { + return kTypeEthernet; + } + if (type.compare(kFlimflamDeviceTypeWifi) == 0) { + return kTypeWifi; + } + if (type.compare(kFlimflamDeviceTypeWimax) == 0) { + return kTypeWimax; + } + if (type.compare(kFlimflamDeviceTypeBluetooth) == 0) { + return kTypeBluetooth; + } + if (type.compare(kFlimflamDeviceTypeGPS) == 0) { + return kTypeGPS; + } + if (type.compare(kFlimflamDeviceTypeCellular) == 0) { + return kTypeCellular; + } + if (type.compare(kFlimflamDeviceTypeVendor) == 0) { + return kTypeVendor; + } + return kTypeUnknown; +} + +void Device::OnCarrierUpdate(const std::string& carrier) { + DLOG(INFO) << path_ << ": OnCarrierUpdate: carrier = " << carrier; + if (carrier_.compare(kCarrierUnknown) && carrier_.compare(carrier)) { + LOG(WARNING) << path_ << ": OnCarrierUpdate: carrier change from " + << carrier_ << " to " << carrier << "!"; + } + // TODO(vlaviano): load backend module for this carrier if needed + carrier_ = carrier; +} + +void Device::OnTypeUpdate(const std::string& type) { + DLOG(INFO) << path_ << ": OnTypeUpdate: type = " << type; + type_ = TypeFromString(type); + if (type_ != kTypeCellular) { + LOG(WARNING) << path_ << ": OnTypeUpdate: type is not cellular!"; + } +} + +void Device::GetDeviceProperties() { + DLOG(INFO) << path_ << ": GetDeviceProperties"; + PropertyMap properties; + // dbus-c++ throws exceptions + // invoke the "Existing Non-conformant Code" clause of the style guide and + // isolate the rest of the system from this + try { + // TODO(vlaviano): make this call asynchronous + properties = GetProperties(); + } catch (DBus::Error& error) { // NOLINT + LOG(WARNING) << path_ + << ": GetDeviceProperties: GetProperties() -> Exception: " + << error.name() << ": " << error.message(); + // TODO(vlaviano): schedule another attempt later + return; + } catch (...) { // NOLINT + LOG(WARNING) << path_ + << ": GetDeviceProperties: GetProperties() -> Exception"; + // TODO(vlaviano): schedule another attempt later + return; + } + DLOG(INFO) << "Received " << properties.size() << " properties"; + + // grab the properties in which we're interested + PropertyMap::const_iterator it; + it = properties.find(kFlimflamDeviceTypeProperty); + if (it != properties.end()) { + const DBus::Variant& value = static_cast<DBus::Variant>(it->second); + OnTypeUpdate(value.reader().get_string()); + } else { + DLOG(WARNING) << path_ << ": GetDeviceProperties: no Type property"; + } + // don't expect to find Cellular.* properties if we're not a cellular device + if (type_ != kTypeCellular) { + return; + } + it = properties.find(kFlimflamDeviceCarrierProperty); + if (it != properties.end()) { + const DBus::Variant& value = static_cast<DBus::Variant>(it->second); + OnCarrierUpdate(value.reader().get_string()); + } else { + DLOG(WARNING) << path_ << ": GetDeviceProperties: no Carrier property"; + } +} + +} // namespace cashew
diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..5fca3b6 --- /dev/null +++ b/src/device.h
@@ -0,0 +1,82 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_DEVICE_H_ +#define SRC_DEVICE_H_ + +#include <string> + +#include <base/basictypes.h> // NOLINT +#include <dbus-c++/dbus.h> // NOLINT + +#include "src/flimflam_device_client_glue.h" + +namespace cashew { + +// represents a cellular device and monitors Flimflam state for that device +class Device : public org::chromium::flimflam::Device_proxy, + public DBus::IntrospectableProxy, + public DBus::ObjectProxy { + public: + Device(DBus::Connection& connection, const DBus::Path& path); // NOLINT + virtual ~Device(); + + // get D-Bus path for this device + virtual const DBus::Path& GetPath() const; + + // valid type values for this device + typedef enum { + kTypeUnknown = 0, + kTypeEthernet, + kTypeWifi, + kTypeWimax, + kTypeBluetooth, + kTypeGPS, + kTypeCellular, + kTypeVendor, + } Type; + + // get type for this device + // NOTE: for now, should always be kTypeCellular + virtual Type GetType() const; + + static const char* kCarrierUnknown; + + // get cellular carrier for this device + // returns kCarrierUnknown if we don't know + virtual const std::string& GetCarrier() const; + + // Flimflam Device D-Bus Proxy methods + virtual void PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value); + + private: + // D-Bus path for Flimflam device that we represent + // this is our unique identifier + const DBus::Path path_; + + // device type + Type type_; + + // cellular carrier + std::string carrier_; + + // convert type string to Type enum value + Type TypeFromString(const std::string& type) const; + + // we've received updated Cellular.Carrier info from Flimflam + void OnCarrierUpdate(const std::string& carrier); + + // we've received updated Type info from Flimflam + void OnTypeUpdate(const std::string& type); + + // get device properties from Flimflam + void GetDeviceProperties(); + + DISALLOW_COPY_AND_ASSIGN(Device); +}; + +} // namespace cashew + +#endif // SRC_DEVICE_H_
diff --git a/src/main.cc b/src/main.cc index 279f954..852bca0 100644 --- a/src/main.cc +++ b/src/main.cc
@@ -14,6 +14,7 @@ #include <glog/logging.h> #include "src/cashew_server.h" +#include "src/service_manager.h" namespace cashew { @@ -35,6 +36,8 @@ static CashewServer *server = NULL; +static ServiceManager *service_manager = NULL; + static void OnSignal(int signal) { DLOG(INFO) << "received signal " << signal; if (dispatcher) { @@ -72,6 +75,8 @@ DLOG(INFO) << "cleaning up"; delete server; server = NULL; + delete service_manager; + service_manager = NULL; DBus::default_dispatcher = NULL; DBus::BusDispatcher *dispatcher_to_delete = dispatcher; dispatcher = NULL; @@ -102,10 +107,22 @@ // TODO(vlaviano): back-end carrier apis + DLOG(INFO) << "creating service manager"; + DBus::Connection client_conn = DBus::Connection::SystemBus(); + cashew::service_manager = new(std::nothrow) cashew::ServiceManager( + client_conn); + if (cashew::service_manager == NULL) { + LOG(ERROR) << "couldn't create service manager"; + cashew::CleanUpForExit(); + exit(EXIT_FAILURE); + } + DLOG(INFO) << "creating cashew server"; - DBus::Connection conn = DBus::Connection::SystemBus(); - conn.request_name(cashew::CashewServer::kServiceName); - cashew::server = new(std::nothrow) cashew::CashewServer(conn); + // TODO(vlaviano): can we share a single conn for client and server roles? + DBus::Connection server_conn = DBus::Connection::SystemBus(); + server_conn.request_name(cashew::CashewServer::kServiceName); + cashew::server = new(std::nothrow) cashew::CashewServer(server_conn, + *cashew::service_manager); if (cashew::server == NULL) { LOG(ERROR) << "couldn't create cashew server"; cashew::CleanUpForExit();
diff --git a/src/service.cc b/src/service.cc new file mode 100644 index 0000000..a05f431 --- /dev/null +++ b/src/service.cc
@@ -0,0 +1,277 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/service.h" + +#include <glog/logging.h> + +#include "src/device.h" + +namespace cashew { + +// Flimflam Service D-Bus identifiers +static const char* kFlimflamServiceName = "org.chromium.flimflam"; + +// Flimflam Service property names +static const char *kFlimflamServiceDeviceProperty = "Device"; +static const char *kFlimflamServiceStateProperty = "State"; +static const char* kFlimflamServiceTypeProperty = "Type"; + +// Flimflam Service on-the-wire State values +static const char* kFlimflamServiceStateIdle = "idle"; +static const char* kFlimflamServiceStateCarrier = "carrier"; +static const char* kFlimflamServiceStateAssociation = "association"; +static const char* kFlimflamServiceStateConfiguration = "configuration"; +static const char* kFlimflamServiceStateReady = "ready"; +static const char* kFlimflamServiceStateDisconnect = "disconnect"; +static const char* kFlimflamServiceStateFailure = "failure"; +static const char* kFlimflamServiceStateActivationFailure = + "activation-failure"; + +// Flimflam Service on-the-wire Type values +static const char* kFlimflamServiceTypeEthernet = "ethernet"; +static const char* kFlimflamServiceTypeWifi = "wifi"; +static const char* kFlimflamServiceTypeWimax = "wimax"; +static const char* kFlimflamServiceTypeBluetooth = "bluetooth"; +static const char* kFlimflamServiceTypeCellular = "cellular"; + +Service::Service(DBus::Connection& connection, // NOLINT + const DBus::Path& path) + : DBus::ObjectProxy(connection, path, kFlimflamServiceName), + connection_(connection), path_(path), state_(kStateUnknown), + type_(kTypeUnknown), device_(NULL) { + // init our state with a GetProperties() call to our Flimflam service path + // we'll update this state by monitoring PropertyChanged signals + GetServiceProperties(); + + // init our data plans list with a hardcoded plan + // TODO(vlaviano): implement backend and return real data + AddHardcodedDataPlan(); +} + +Service::~Service() { + DeleteDataPlans(); + if (device_ != NULL) { + DLOG(INFO) << path_ << ": deleting device " << device_->GetPath(); + delete device_; + device_ = NULL; + } +} + +const DBus::Path& Service::GetPath() const { + return path_; +} + +Service::State Service::GetState() const { + return state_; +} + +Service::Type Service::GetType() const { + return type_; +} + +Device* Service::GetDevice() const { + return device_; +} + +DBusDataPlanList Service::GetDBusDataPlans() const { + DBusDataPlanList dbus_data_plans; + DataPlanList::const_iterator it; + for (it = data_plans_.begin(); it != data_plans_.end(); ++it) { + DataPlan *plan = *it; + DCHECK(plan != NULL); + dbus_data_plans.push_back(plan->ToDBusFormat()); + } + return dbus_data_plans; +} + +// Flimflam Service D-Bus Proxy methods + +void Service::PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value) { + DLOG(INFO) << path_ << ": PropertyChanged: property_name = " << property_name; + if (property_name.compare(kFlimflamServiceDeviceProperty) == 0) { + OnDeviceUpdate(new_value.reader().get_path()); + } else if (property_name.compare(kFlimflamServiceStateProperty) == 0) { + OnStateUpdate(new_value.reader().get_string()); + } else if (property_name.compare(kFlimflamServiceTypeProperty) == 0) { + OnTypeUpdate(new_value.reader().get_string()); + } else { + // we don't care about this property + } +} + +// Private methods + +Service::State Service::StateFromString(const std::string& state) const { + if (state.compare(kFlimflamServiceStateIdle) == 0) { + return kStateIdle; + } + if (state.compare(kFlimflamServiceStateCarrier) == 0) { + return kStateCarrier; + } + if (state.compare(kFlimflamServiceStateAssociation) == 0) { + return kStateAssociation; + } + if (state.compare(kFlimflamServiceStateConfiguration) == 0) { + return kStateConfiguration; + } + if (state.compare(kFlimflamServiceStateReady) == 0) { + return kStateReady; + } + if (state.compare(kFlimflamServiceStateDisconnect) == 0) { + return kStateDisconnect; + } + if (state.compare(kFlimflamServiceStateFailure) == 0) { + return kStateFailure; + } + if (state.compare(kFlimflamServiceStateActivationFailure) == 0) { + return kStateActivationFailure; + } + return kStateUnknown; +} + +Service::Type Service::TypeFromString(const std::string& type) const { + if (type.compare(kFlimflamServiceTypeEthernet) == 0) { + return kTypeEthernet; + } + if (type.compare(kFlimflamServiceTypeWifi) == 0) { + return kTypeWifi; + } + if (type.compare(kFlimflamServiceTypeWimax) == 0) { + return kTypeWimax; + } + if (type.compare(kFlimflamServiceTypeBluetooth) == 0) { + return kTypeBluetooth; + } + if (type.compare(kFlimflamServiceTypeCellular) == 0) { + return kTypeCellular; + } + return kTypeUnknown; +} + +void Service::OnDeviceUpdate(const DBus::Path& device_path) { + DLOG(INFO) << path_ << ": OnDeviceUpdate: device_path = " << device_path; + + // if there's an existing device with a non-matching path: destroy it and + // fall through to make a new one below. + if (device_ != NULL && device_->GetPath().compare(device_path)) { + LOG(WARNING) << path_ << ": OnDeviceUpdate: device path changed from " + << device_->GetPath() << " to " << device_path; + delete device_; + device_ = NULL; + } + + // if there's an existing device with matching path: all is well + if (device_ != NULL) { + return; + } + + // if there's no existing device: make one + device_ = new(std::nothrow) Device(connection_, device_path); + if (device_ == NULL) { + LOG(ERROR) << path_ << ": OnDeviceUpdate: couldn't create device for " + << device_path; + return; + } + DLOG(INFO) << path_ << ": OnDeviceUpdate: created device for " + << device_path; +} + +void Service::OnStateUpdate(const std::string& state) { + DLOG(INFO) << path_ << ": OnStateUpdate: state = " << state; + state_ = StateFromString(state); + // TODO(vlaviano): react to state changes + // note that ready does not necessarily mean that we're the default route +} + +void Service::OnTypeUpdate(const std::string& type) { + DLOG(INFO) << path_ << ": OnTypeUpdate: type = " << type; + type_ = TypeFromString(type); + if (type_ != kTypeCellular) { + LOG(WARNING) << path_ << ": OnTypeUpdate: type is not cellular!"; + } +} + +void Service::GetServiceProperties() { + DLOG(INFO) << path_ << ": GetServiceProperties"; + PropertyMap properties; + // dbus-c++ throws exceptions + // invoke the "Existing Non-conformant Code" clause of the style guide and + // isolate the rest of the system from this + try { + // TODO(vlaviano): make this call asynchronous + properties = GetProperties(); + } catch (DBus::Error& error) { // NOLINT + LOG(WARNING) << path_ + << ": GetServiceProperties: GetProperties() -> Exception: " + << error.name() << ": " << error.message(); + // TODO(vlaviano): schedule another attempt later + return; + } catch (...) { // NOLINT + LOG(WARNING) << path_ + << ": GetServiceProperties: GetProperties() -> Exception"; + // TODO(vlaviano): schedule another attempt later + return; + } + DLOG(INFO) << "Received " << properties.size() << " properties"; + + // grab the properties in which we're interested + PropertyMap::const_iterator it; + it = properties.find(kFlimflamServiceDeviceProperty); + if (it != properties.end()) { + const DBus::Variant& value = static_cast<DBus::Variant>(it->second); + OnDeviceUpdate(value.reader().get_path()); + } else { + DLOG(WARNING) << path_ << ": GetServiceProperties: no Device property"; + } + it = properties.find(kFlimflamServiceStateProperty); + if (it != properties.end()) { + const DBus::Variant& value = static_cast<DBus::Variant>(it->second); + OnStateUpdate(value.reader().get_string()); + } else { + DLOG(WARNING) << path_ << ": GetServiceProperties: no State property"; + } + it = properties.find(kFlimflamServiceTypeProperty); + if (it != properties.end()) { + const DBus::Variant& value = static_cast<DBus::Variant>(it->second); + OnTypeUpdate(value.reader().get_string()); + } else { + DLOG(WARNING) << path_ << ": GetServiceProperties: no Type property"; + } +} + +void Service::DeleteDataPlans() { + while (!data_plans_.empty()) { + DataPlan *plan = *data_plans_.begin(); + DCHECK(plan != NULL); + DLOG(INFO) << path_ << ": DeleteDataPlans: deleting plan: " + << plan->GetName(); + delete plan; + data_plans_.erase(data_plans_.begin()); + } +} + +void Service::AddHardcodedDataPlan() { + const std::string name = "Chromium OS Test Plan"; + DataPlan::Type type = DataPlan::kTypeMeteredPaid; + base::Time update_time = base::Time::Now(); + base::TimeDelta twelve_hours = base::TimeDelta::FromHours(12); + base::Time start_time = base::Time::Now() - twelve_hours; + base::Time end_time = base::Time::Now() + twelve_hours; + Bytes data_bytes_max = 100*1024*1024; // 100 MB + Bytes data_bytes_used = 30*1024*1024; // 30 MB + + DataPlan *plan = new(std::nothrow) DataPlan(name, type, update_time, + start_time, end_time, + data_bytes_max, data_bytes_used); + if (plan == NULL) { + LOG(ERROR) << path_ << ": AddHardcodedDataPlan: could not create plan"; + return; + } + + data_plans_.push_back(plan); +} + +} // namespace cashew
diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..8edf613 --- /dev/null +++ b/src/service.h
@@ -0,0 +1,132 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_SERVICE_H_ +#define SRC_SERVICE_H_ + +#include <map> +#include <string> + +#include <base/basictypes.h> // NOLINT +#include <dbus-c++/dbus.h> // NOLINT + +#include "src/data_plan.h" +#include "src/flimflam_service_client_glue.h" + +namespace cashew { + +class Device; + +// map of key, value pairs representing service properties received via D-Bus +typedef std::map<std::string, DBus::Variant> PropertyMap; + +// represents a cellular service and monitors Flimflam state for that service +class Service : public org::chromium::flimflam::Service_proxy, + public DBus::IntrospectableProxy, + public DBus::ObjectProxy { + public: + Service(DBus::Connection& connection, const DBus::Path& path); // NOLINT + virtual ~Service(); + + // get D-Bus path for this service + virtual const DBus::Path& GetPath() const; + + // valid state values for this service + typedef enum { + kStateUnknown = 0, + kStateIdle, + kStateCarrier, + kStateAssociation, + kStateConfiguration, + kStateReady, + kStateDisconnect, + kStateFailure, + kStateActivationFailure, + } State; + + // get state for this service + virtual State GetState() const; + + // valid type values for this service + typedef enum { + kTypeUnknown = 0, + kTypeEthernet, + kTypeWifi, + kTypeWimax, + kTypeBluetooth, + kTypeCellular, + } Type; + + // get type for this service + // NOTE: for now, should always be kTypeCellular + virtual Type GetType() const; + + // get Device object for this service + // this can return NULL if we don't yet have Device info + virtual Device* GetDevice() const; + + // get data plan info, formatted for D-Bus + virtual DBusDataPlanList GetDBusDataPlans() const; + + // Flimflam Service D-Bus Proxy methods + + // receive incoming PropertyChanged D-Bus signal from Flimflam service + virtual void PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value); + + private: + // D-Bus connection, owned by our creator + // shared with Device obj that we create + DBus::Connection& connection_; + + // D-Bus path for Flimflam service that we represent + // this is our unique identifier + const DBus::Path path_; + + // Service connection state + State state_; + + // Service type + Type type_; + + // Device corresponding to our service + Device *device_; + + // cached data plan info + DataPlanList data_plans_; + + // TODO(vlaviano): reference to back end carrier plugin + // TODO(vlaviano): refresh data plan info from carrier plugin + + // convert state string to State enum value + State StateFromString(const std::string& state) const; + + // convert type string to Type enum value + Type TypeFromString(const std::string& type) const; + + // we've received updated Device info from Flimflam + void OnDeviceUpdate(const DBus::Path& device_path); + + // we've received updated State info from Flimflam + void OnStateUpdate(const std::string& state); + + // we've received updated Type info from Flimflam + void OnTypeUpdate(const std::string& type); + + // get service properties from Flimflam + void GetServiceProperties(); + + // clear data_plans_ list and delete data plan objects + void DeleteDataPlans(); + + // create a hardcoded data plan and add it to data_plans_ list + // for debugging + void AddHardcodedDataPlan(); + + DISALLOW_COPY_AND_ASSIGN(Service); +}; + +} // namespace cashew + +#endif // SRC_SERVICE_H_
diff --git a/src/service_manager.cc b/src/service_manager.cc new file mode 100644 index 0000000..39f3c6d --- /dev/null +++ b/src/service_manager.cc
@@ -0,0 +1,171 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/service_manager.h" + +#include <glog/logging.h> + +#include "src/service.h" + +namespace cashew { + +// Flimflam D-Bus indentifiers +static const char* kFlimflamManagerName = "org.chromium.flimflam"; +static const char* kFlimflamManagerPath = "/"; + +// Flimflam property names +static const char* kServicesProperty = "Services"; + +// Flimflam service types +static const char* kTypeCellular = "cellular"; + +ServiceManager::ServiceManager(DBus::Connection& connection) // NOLINT + : DBus::ObjectProxy(connection, kFlimflamManagerPath, + kFlimflamManagerName), + connection_(connection) { + // init our state with a GetProperties() call to Flimflam + // we'll update this state by monitoring PropertyChanged signals + GetFlimflamServices(); +} + +ServiceManager::~ServiceManager() { + DeleteServices(&services_); +} + +const Service* ServiceManager::GetService(const std::string& service_path) + const { + ServiceMap::const_iterator it = services_.find(service_path); + if (it == services_.end()) { + return NULL; + } + DCHECK(it->second != NULL); + return static_cast<const Service *>(it->second); +} + +// Flimflam Manager D-Bus Proxy methods + +void ServiceManager::PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value) { + DLOG(INFO) << "PropertyChanged: property_name = " << property_name; + + // we only care about Services changes + if (property_name.compare(kServicesProperty)) { + return; + } + + // interpret new_value as a vector of service paths + ServicePathList paths; + DBus::MessageIter reader = new_value.reader(); + reader >> paths; + + OnServicesUpdate(paths); +} + +void ServiceManager::StateChanged(const std::string& new_state) { + DLOG(INFO) << "StateChanged: new_state = " << new_state; + // TODO(vlaviano): switch on state and call private onFlimflamOffline(), + // onFlimflamOnline() hooks. If we care. +} + +// Private methods + +void ServiceManager::DeleteServices(ServiceMap *service_map) { + DCHECK(service_map != NULL); + while (!service_map->empty()) { + const std::string& path = + static_cast<std::string>(service_map->begin()->first); + DLOG(INFO) << "DeleteServices: deleting service: " << path; + Service *service = static_cast<Service *>(service_map->begin()->second); + delete service; + service_map->erase(service_map->begin()); + } +} + +void ServiceManager::GetFlimflamServices() { + DLOG(INFO) << "GetFlimflamServices"; + PropertyMap properties; + // dbus-c++ throws exceptions + // invoke the "Existing Non-conformant Code" clause of the style guide and + // isolate the rest of the system from this + try { + // TODO(vlaviano): make this call asynchronous + properties = GetProperties(); + } catch (DBus::Error& error) { // NOLINT + LOG(WARNING) << "GetFlimflamServices: GetProperties() -> Exception: " + << error.name() << ": " << error.message(); + // TODO(vlaviano): schedule another attempt later + return; + } catch (...) { // NOLINT + LOG(WARNING) << "GetFlimflamServices: GetProperties() -> Exception"; + // TODO(vlaviano): schedule another attempt later + return; + } + + // grab Services property from result + PropertyMap::const_iterator it = properties.find(kServicesProperty); + if (it == properties.end()) { + DLOG(WARNING) << "GetFlimflamServices: no Services property"; + return; + } + DBus::Variant value = static_cast<DBus::Variant>(it->second); + + // interpret value as vector of service paths + ServicePathList paths; + DBus::MessageIter reader = value.reader(); + reader >> paths; + + OnServicesUpdate(paths); +} + +void ServiceManager::OnServicesUpdate(const ServicePathList& paths) { + // Remember old set of services. This is the baseline from which we will + // determine what has changed. + ServiceMap old_services = services_; + services_.clear(); + + // Flimflam service types are embedded in their path names, so we can use + // the pattern "/cellular_" to decide which paths are cellular services. + static const std::string cellular_pattern = std::string("/") + kTypeCellular + + "_"; + + // Walk paths in this update and see if they're new to us + DLOG(INFO) << "OnServicesUpdate: " << paths.size() << " service paths"; + ServicePathList::const_iterator it; + for (it = paths.begin(); it != paths.end(); ++it) { + const DBus::Path& path = *it; + + // We only care about cellular services + if (path.find(cellular_pattern) == std::string::npos) { + continue; + } + + ServiceMap::iterator old_it = old_services.find(path); + if (old_it != old_services.end()) { + // service is old: move it from old_services to services_ + Service *service = static_cast<Service *>(old_it->second); + DCHECK(service != NULL); + old_services.erase(old_it); + services_[path] = service; + } else { + // service is new: create a Service obj for it and add it to services_ + OnNewService(path); + } + } + + // services left in old_services failed to appear in the update: delete them + DeleteServices(&old_services); +} + +void ServiceManager::OnNewService(const DBus::Path& path) { + DLOG(INFO) << "OnNewService: " << path; + DCHECK(GetService(path) == NULL); + Service *service = new(std::nothrow) Service(connection_, path); + if (service == NULL) { + LOG(ERROR) << "OnNewService: couldn't create service for " << path; + return; + } + services_[path] = service; +} + +} // namespace cashew
diff --git a/src/service_manager.h b/src/service_manager.h new file mode 100644 index 0000000..4eae79e --- /dev/null +++ b/src/service_manager.h
@@ -0,0 +1,73 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_SERVICE_MANAGER_H_ +#define SRC_SERVICE_MANAGER_H_ + +#include <map> +#include <string> +#include <vector> + +#include <base/basictypes.h> // NOLINT +#include <dbus-c++/dbus.h> // NOLINT + +#include "src/flimflam_manager_client_glue.h" + +namespace cashew { + +class Service; + +// map of service path names to Service objects +typedef std::map<std::string, Service*> ServiceMap; + +// vector of DBus::Path objects (strings) representing service path names +typedef std::vector<DBus::Path> ServicePathList; + +// monitors Flimflam and maintains a collection of cellular service objects +class ServiceManager : public org::chromium::flimflam::Manager_proxy, + public DBus::IntrospectableProxy, + public DBus::ObjectProxy { + public: + explicit ServiceManager(DBus::Connection& connection); // NOLINT + virtual ~ServiceManager(); + + // look up a service by its path name + // returns NULL if service is not found + virtual const Service* GetService(const std::string& service_path) const; + + // Flimflam Manager D-Bus Proxy methods + + // receive incoming PropertyChanged D-Bus signal from Flimflam + virtual void PropertyChanged(const std::string& property_name, + const DBus::Variant& new_value); + + // receive incoming StateChanged D-Bus signal from Flimflam + virtual void StateChanged(const std::string& new_state); + + private: + // D-Bus connection owned by our creator + // shared with Service objs that we create + DBus::Connection& connection_; + + // collection of Service objs representing cellular services + ServiceMap services_; + + // delete a collection of services + void DeleteServices(ServiceMap *service_map); + + // get Services info from Flimflam + void GetFlimflamServices(); + + // we've received updated Services info from Flimflam + void OnServicesUpdate(const ServicePathList& paths); + + // Flimflam has started advertising a new service + void OnNewService(const DBus::Path& path); + + DISALLOW_COPY_AND_ASSIGN(ServiceManager); +}; + +} // namespace cashew + +#endif // SRC_SERVICE_MANAGER_H_