cashew: send usage requests via cellular interface

cashew currently sends usage requests via the default service. Since
requests must be sent over-the-air, this forces us to send requests for
a cellular service only when it's the default service.

This CL changes cashew to send requests for a cellular service over the
interface corresponding to that service, whether or not it's the default
service. This allows us to retrieve and display plan info as soon as the
cellular service is connected.

We accomplish this by changing flimflam to install a host route that
causes all traffic to the usage API to go out via the cellular
interface (see issue 17920) and monitoring the service's StickyHostRoute
property from cashew. If a service has a sticky host route, we no longer
require it to be the default service before sending usage requests.

BUG=chromium-os:16250
TEST=Ran unit tests and performed manual testing on device

Change-Id: Ib248aac6f254a5b4e3a070ddd05969538331e999
Reviewed-on: https://gerrit.chromium.org/gerrit/7184
Tested-by: Vince Laviano <vlaviano@chromium.org>
Reviewed-by: Jason Glasgow <jglasgow@chromium.org>
Commit-Ready: Vince Laviano <vlaviano@chromium.org>
diff --git a/src/service_impl.cc b/src/service_impl.cc
index 8d120bc..1eb975a 100644
--- a/src/service_impl.cc
+++ b/src/service_impl.cc
@@ -23,6 +23,7 @@
 static const char *kFlimflamServiceStateProperty = "State";
 static const char *kFlimflamServiceTypeProperty = "Type";
 static const char *kFlimflamServiceUsageUrlProperty = "Cellular.UsageUrl";
+static const char *kFlimflamServiceStickyHostRouteProperty = "StickyHostRoute";
 
 // Flimflam Service on-the-wire State values
 static const char *kFlimflamServiceStateIdle = "idle";
@@ -204,6 +205,8 @@
     OnTypeUpdate(new_value.reader().get_string());
   } else if (property_name == kFlimflamServiceUsageUrlProperty) {
     OnUsageUrlUpdate(new_value.reader().get_string());
+  } else if (property_name == kFlimflamServiceStickyHostRouteProperty) {
+    OnStickyHostRouteUpdate(new_value.reader().get_string());
   } else {
     // we don't care about this property
   }
@@ -610,6 +613,16 @@
   }
 }
 
+void ServiceImpl::OnStickyHostRouteUpdate(const std::string& sticky_route) {
+  LOG(INFO) << path_ << ": OnStickyHostRouteUpdate: sticky route = "
+      << sticky_route;
+  if (sticky_route == sticky_host_route_) {
+    return;
+  }
+  sticky_host_route_ = sticky_route;
+  ReconsiderSendingUsageRequests();
+}
+
 // static
 gboolean ServiceImpl::StaticGetServicePropertiesCallback(gpointer data) {
   ServiceImpl *service = reinterpret_cast<ServiceImpl*>(data);
@@ -691,6 +704,14 @@
   } else {
     LOG(WARNING) << path_ << ": GetServiceProperties: no Type property";
   }
+  it = properties.find(kFlimflamServiceStickyHostRouteProperty);
+  if (it != properties.end()) {
+    const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
+    OnStickyHostRouteUpdate(value.reader().get_string());
+  } else {
+    LOG(WARNING) << path_
+        << ": GetServiceProperties: no StickyHostRoute property";
+  }
   // don't expect to find Cellular.* properties if we're not a cellular service
   if (type_ != kTypeCellular) {
     return true;
@@ -907,8 +928,6 @@
   return request_in_progress_ || update_timeout_source_ != NULL;
 }
 
-// NOTE: we centralize our decisionmaking here to avoid insanity and just call
-// ReconsiderSendingUsageRequests when anything changes
 bool ServiceImpl::ShouldSendUsageRequests() const {
   if (usage_request_complete_) {
     LOG(INFO) << path_ <<
@@ -931,8 +950,9 @@
     LOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not connected";
     return false;
   }
-  if (!IsDefaultService()) {
-    LOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not default service";
+  if (sticky_host_route_.empty() && !IsDefaultService()) {
+    LOG(INFO) << path_ << ": ShouldSendUsageRequests: "
+        << "no: no sticky host route and not default service";
     return false;
   }
   LOG(INFO) << path_ << ": ShouldSendUsageRequests: yes";
diff --git a/src/service_impl.h b/src/service_impl.h
index 66d1dbd..12f0fa8 100644
--- a/src/service_impl.h
+++ b/src/service_impl.h
@@ -163,6 +163,10 @@
     // See comments in service_manager.h
     PropertyChangedHandler property_changed_handler_;
 
+    // Dotted decimal IP addr for which service has a sticky host route
+    // empty string represents "none"
+    std::string sticky_host_route_;
+
     // convert state string to State enum value
     static State StateFromString(const std::string& state);
 
@@ -178,6 +182,9 @@
     // we've received updated Cellular.UsageUrl info from Flimflam
     void OnUsageUrlUpdate(const std::string& usage_url);
 
+    // We've received updated StickyHostRoute info from Flimflam
+    void OnStickyHostRouteUpdate(const std::string& sticky_host_route);
+
     // glib integration: static wrapper for GetServiceProperties
     // takes object ptr as data and invokes object->GetServiceProperties()
     static gboolean StaticGetServicePropertiesCallback(gpointer data);