Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(295)

Unified Diff: src/service.cc

Issue 3528016: Cashew: implement backend usage API (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/cashew.git
Patch Set: Fix code review nits and remove hardcoded usage URLs Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/service.h ('k') | src/service_manager.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/service.cc
diff --git a/src/service.cc b/src/service.cc
index a05f431705f3f92e7c81bf2e96d7eff053f5ab22..e2562ee7322f2b6ae8d0a41b63ad53f1278c2695 100644
--- a/src/service.cc
+++ b/src/service.cc
@@ -6,52 +6,70 @@
#include <glog/logging.h>
+#include "src/data_plan_provider.h"
#include "src/device.h"
+#include "src/policy.h"
+#include "src/service_manager.h"
namespace cashew {
// Flimflam Service D-Bus identifiers
-static const char* kFlimflamServiceName = "org.chromium.flimflam";
+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";
+static const char *kFlimflamServiceTypeProperty = "Type";
+static const char *kFlimflamServiceUsageUrlProperty = "Cellular.UsageUrl";
// 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 =
+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
+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";
+
+// Chromium OS Usage API property names
+static const char *kCrosUsageVersionProperty = "version";
+static const char *kCrosUsageStatusProperty = "status";
+static const char *kCrosUsageRestrictedProperty = "restricted";
+static const char *kCrosUsagePlansProperty = "plans";
+
+// Chromium OS Usage API version values
+static const int kCrosUsageVersionMinSupported = 1;
+static const int kCrosUsageVersionMaxSupported = 1;
+
+// Chromium OS Usage API status values
+static const char *kCrosUsageStatusOk = "OK";
+static const char *kCrosUsageStatusError = "ERROR";
+
+Service::Service(ServiceManager * const parent,
+ DBus::Connection& connection, // NOLINT
const DBus::Path& path)
: DBus::ObjectProxy(connection, path, kFlimflamServiceName),
- connection_(connection), path_(path), state_(kStateUnknown),
- type_(kTypeUnknown), device_(NULL) {
+ parent_(CHECK_NOTNULL(parent)), connection_(connection), path_(path),
+ state_(kStateUnknown), type_(kTypeUnknown), device_(NULL),
+ provider_(NULL), request_in_progress_(false),
+ update_timeout_source_(NULL), policy_(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();
+ DeleteCarrierState();
+ DeleteDataPlans(&data_plans_);
if (device_ != NULL) {
DLOG(INFO) << path_ << ": deleting device " << device_->GetPath();
delete device_;
@@ -97,11 +115,172 @@ void Service::PropertyChanged(const std::string& property_name,
OnStateUpdate(new_value.reader().get_string());
} else if (property_name.compare(kFlimflamServiceTypeProperty) == 0) {
OnTypeUpdate(new_value.reader().get_string());
+ } else if (property_name.compare(kFlimflamServiceUsageUrlProperty) == 0) {
+ OnUsageUrlUpdate(new_value.reader().get_string());
} else {
// we don't care about this property
}
}
+// Device methods
+
+void Service::OnCarrierUpdate(const std::string& carrier) {
+ DLOG(INFO) << path_ << ": OnCarrierUpdate: carrier = " << carrier;
+ // NOTE: device_ can be NULL here because we may still be in Device ctor
+ DCHECK(device_ == NULL || carrier.compare(device_->GetCarrier()) == 0);
+
+ // get rid of state associated with old carrier
+ if (provider_ != NULL) {
+ DCHECK(carrier.compare(provider_->GetCarrier()));
+ }
+ DeleteCarrierState();
+
+ // if we don't have new carrier info, we can't do anything now
+ if (carrier.compare(Device::kCarrierUnknown) == 0) {
+ return;
+ }
+
+ // create state for new carrier
+ DLOG(INFO) << path_ << ": OnCarrierUpdate: creating data plan provider";
+ provider_ = new(std::nothrow) DataPlanProvider(carrier);
+ if (provider_ == NULL) {
+ LOG(ERROR) << path_
+ << ": OnCarrierUpdate: could not create data plan provider";
+ return;
+ }
+ provider_->SetDelegate(this);
+ DLOG(INFO) << path_ << ": OnCarrierUpdate: getting policy for " << carrier;
+ policy_ = Policy::GetPolicy(carrier);
+ if (policy_ == NULL) {
+ LOG(ERROR) << path_ << ": OnCarrierUpdate: could not get policy for "
+ << carrier;
+ DeleteCarrierState();
+ return;
+ }
+
+ // if we already have usage url, set new provider in motion
+ if (!usage_url_.empty()) {
+ provider_->SetUsageUrl(usage_url_);
+ RequestUsageUpdate();
+ CreateUpdateTimer();
+ } else {
+ // we'll do this later in OnUsageUrlUpdate
+ }
+}
+
+// DataPlanProviderDelegate methods
+
+void Service::OnRequestComplete(const DataPlanProvider *provider,
+ bool successful,
+ const Value *parsed_usage_update) {
+ DCHECK(provider != NULL);
+ DCHECK(!successful || parsed_usage_update != NULL);
+ if (provider != provider_) {
+ DLOG(WARNING) << path_ << ": OnRequestComplete: wrong provider";
+ return;
+ }
+ DCHECK(request_in_progress_);
+ DCHECK(policy_ != NULL);
+ DLOG(INFO) << path_ << ": OnRequestComplete: result = " << successful;
+ request_in_progress_ = false;
+ if (!successful) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: request failed";
+ return;
+ }
+
+ // interpret and validate parsed usage update
+ if (!parsed_usage_update->IsType(Value::TYPE_DICTIONARY)) {
+ LOG(WARNING) << path_
+ << ": OnRequestComplete: root value is not a dictionary";
+ return;
+ }
+ const DictionaryValue *root =
+ static_cast<const DictionaryValue*>(parsed_usage_update);
+
+ int version;
+ if (!root->GetInteger(kCrosUsageVersionProperty, &version)) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: no version property";
+ return;
+ }
+ DLOG(INFO) << path_ << ": OnRequestComplete: version = " << version;
+ if (version < kCrosUsageVersionMinSupported ||
+ version > kCrosUsageVersionMaxSupported) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: version " << version
+ << " not in supported range of [" << kCrosUsageVersionMinSupported
+ << ", " << kCrosUsageVersionMaxSupported << "]";
+ return;
+ }
+
+ std::string status;
+ if (!root->GetString(kCrosUsageStatusProperty, &status)) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: no status property";
+ return;
+ }
+ DLOG(INFO) << path_ << ": OnRequestComplete: status = " << status;
+ if (status != kCrosUsageStatusOk && status != kCrosUsageStatusError) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: invalid status: " << status;
+ return;
+ }
+ if (status == kCrosUsageStatusError) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: status: " << status;
+ return;
+ }
+
+ bool restricted = false;
+ if (root->GetBoolean(kCrosUsageRestrictedProperty, &restricted)) {
+ DLOG(INFO) << path_ << ": OnRequestComplete: restricted = " << restricted;
+ } else {
+ DLOG(INFO) << path_ << ": OnRequestComplete: no restricted property";
+ // restricted property is optional
+ }
+
+ ListValue *plans = NULL; // list to which this points is owned by dictionary
+ if (!root->GetList(kCrosUsagePlansProperty, &plans)) {
+ LOG(WARNING) << path_ << ": OnRequestComplete: no plans property";
+ return;
+ }
+ DCHECK(plans != NULL);
+ size_t plans_size = plans->GetSize();
+ DLOG(INFO) << path_ << ": OnRequestComplete: plans list has size "
+ << plans_size;
+
+ // walk plans list and convert each into a DataPlan object
+ DataPlanList new_plans;
+ for (size_t i = 0; i < plans_size; ++i) {
+ DictionaryValue *plan_dict = NULL;
+ if (!plans->GetDictionary(i, &plan_dict)) {
+ LOG(WARNING) << path_
+ << ": OnRequestComplete: could not get plan[" << i << "]";
+ continue;
+ }
+ DCHECK(plan_dict != NULL);
+ DataPlan *plan = DataPlan::FromDictionaryValue(plan_dict);
+ if (plan == NULL) {
+ LOG(WARNING) << path_
+ << ": OnRequestComplete: could not convert plan[" << i << "]";
+ continue;
+ }
+ DLOG(INFO) << path_ << ": OnRequestComplete: converted plan[" << i << "]";
+ new_plans.push_back(plan);
+ }
+
+ // store new info, causing it to be used for subsequent updates to clients
+ DataPlanList old_plans = data_plans_;
+ data_plans_ = new_plans;
+ LOG(INFO) << path_ << ": OnRequestComplete: updated data plans";
+
+ // consult policy to determine if we should send an unsolicited update
+ if (policy_->ShouldEmitDataPlansUpdate(old_plans, new_plans)) {
+ DLOG(INFO) << path_ << ": OnRequestComplete: sending update";
+ parent_->EmitDataPlansUpdate(*this);
+ } else {
+ DLOG(INFO) << path_ << ": OnRequestComplete: not sending update";
+ }
+
+ DLOG(INFO) << path_ << ": OnRequestComplete: deleting old data plans";
+ DeleteDataPlans(&old_plans);
+}
+
// Private methods
Service::State Service::StateFromString(const std::string& state) const {
@@ -169,7 +348,7 @@ void Service::OnDeviceUpdate(const DBus::Path& device_path) {
}
// if there's no existing device: make one
- device_ = new(std::nothrow) Device(connection_, device_path);
+ device_ = new(std::nothrow) Device(this, connection_, device_path);
if (device_ == NULL) {
LOG(ERROR) << path_ << ": OnDeviceUpdate: couldn't create device for "
<< device_path;
@@ -194,6 +373,22 @@ void Service::OnTypeUpdate(const std::string& type) {
}
}
+void Service::OnUsageUrlUpdate(const std::string& usage_url) {
+ DLOG(INFO) << path_ << ": OnUsageUrlUpdate: url = " << usage_url;
+ if (usage_url.compare(usage_url_) == 0) {
+ return;
+ }
+ usage_url_ = usage_url;
+ if (provider_ != NULL) {
+ DCHECK(usage_url.compare(provider_->GetUsageUrl()));
+ DestroyUpdateTimer();
+ CancelPendingRequests();
+ provider_->SetUsageUrl(usage_url);
+ RequestUsageUpdate();
+ CreateUpdateTimer();
+ }
+}
+
void Service::GetServiceProperties() {
DLOG(INFO) << path_ << ": GetServiceProperties";
PropertyMap properties;
@@ -240,16 +435,29 @@ void Service::GetServiceProperties() {
} else {
DLOG(WARNING) << path_ << ": GetServiceProperties: no Type property";
}
+ // don't expect to find Cellular.* properties if we're not a cellular service
+ if (type_ != kTypeCellular) {
+ return;
+ }
+ it = properties.find(kFlimflamServiceUsageUrlProperty);
+ if (it != properties.end()) {
+ const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
+ OnUsageUrlUpdate(value.reader().get_string());
+ } else {
+ DLOG(WARNING) << path_
+ << ": GetServiceProperties: no Cellular.UsageUrl property";
+ }
}
-void Service::DeleteDataPlans() {
- while (!data_plans_.empty()) {
- DataPlan *plan = *data_plans_.begin();
+void Service::DeleteDataPlans(DataPlanList *data_plans) {
+ DCHECK(data_plans != NULL);
+ 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());
+ data_plans->erase(data_plans->begin());
}
}
@@ -274,4 +482,90 @@ void Service::AddHardcodedDataPlan() {
data_plans_.push_back(plan);
}
+void Service::RequestUsageUpdate() {
+ DCHECK(provider_ != NULL);
+ DCHECK(!usage_url_.empty());
+ DCHECK(device_ == NULL ||
+ device_->GetCarrier().compare(Device::kCarrierUnknown));
+
+ if (request_in_progress_) {
+ return;
+ }
+
+ // TODO(vlaviano): apply policy to determine if current connectivity state
+ // allows this request (e.g., if we can only make API requests over the
+ // cellular service itself, is this service the default route?)
+
+ if (!provider_->RequestUsageUpdate()) {
+ DLOG(WARNING) << path_ << ": RequestUsageUpdate: request failed";
+ } else {
+ DLOG(INFO) << path_ << ": RequestUsageUpdate: request succeeded";
+ request_in_progress_ = true;
+ }
+}
+
+void Service::CancelPendingRequests() {
+ if (provider_ != NULL && request_in_progress_) {
+ provider_->CancelPendingRequests();
+ request_in_progress_ = false;
+ }
+}
+
+gboolean Service::UpdateTimeoutCallback() {
+ DLOG(INFO) << path_ << ": UpdateTimeoutCallback";
+ RequestUsageUpdate();
+ return TRUE;
+}
+
+// static
+gboolean Service::StaticUpdateTimeoutCallback(gpointer data) {
+ CHECK_NOTNULL(data);
+ return reinterpret_cast<Service*>(data)->UpdateTimeoutCallback();
+}
+
+bool Service::CreateUpdateTimer() {
+ DCHECK(provider_ != NULL);
+ DCHECK(policy_ != NULL);
+ guint idle_seconds = policy_->GetUpdateTimerIdleSecs(data_plans_);
+ DLOG(INFO) << path_ << ": CreateUpdateTimer: idle_seconds = " << idle_seconds;
+ if (update_timeout_source_ != NULL) {
+ DLOG(WARNING) << path_ << ": CreateUpdateTimer: timer already running";
+ return false;
+ }
+ update_timeout_source_ = g_timeout_source_new_seconds(idle_seconds);
+ if (update_timeout_source_ == NULL) {
+ DLOG(WARNING) << path_
+ << ": CreateUpdateTimer: could not create timeout source";
+ return false;
+ }
+ g_source_set_callback(update_timeout_source_, StaticUpdateTimeoutCallback,
+ this, NULL);
+ g_source_attach(update_timeout_source_, NULL);
+ return true;
+}
+
+void Service::DestroyUpdateTimer() {
+ DLOG(INFO) << path_ << ": DestroyUpdateTimer";
+ if (update_timeout_source_ != NULL) {
+ g_source_destroy(update_timeout_source_);
+ update_timeout_source_ = NULL;
+ }
+}
+
+void Service::DeleteCarrierState() {
+ DLOG(INFO) << path_ << ": DeleteCarrierState";
+ if (policy_ != NULL) {
+ DLOG(INFO) << path_ << ": DeleteCarrierState: deleting policy";
+ delete policy_;
+ policy_ = NULL;
+ }
+ DestroyUpdateTimer();
+ CancelPendingRequests();
+ if (provider_ != NULL) {
+ DLOG(INFO) << path_ << ": DeleteCarrierState: deleting data plan provider";
+ delete provider_;
+ provider_ = NULL;
+ }
+}
+
} // namespace cashew
« no previous file with comments | « src/service.h ('k') | src/service_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698