Treat confirmed Tethered networks as Cellular networks.

The updates are allowed based on the type of connection used to
download it from. For example, by default, no update is made over
Cellular networks even if the device is connected to a VPN over a
Cellular network to prevent huge charges on those connections.

Nevertheless, when the device is connected to a tethered network such
as an Android or iPhone sharing its Cellular connection over Wifi,
the connection type the device sees is a Wifi and thus will allow
the updates by default.

To prevent updates over tethered networks, this patch uses the
Tethering property expossed by shill to avoid those situations. If
the device is connected to a network that shill confirms to be a
tethered network, it will be treated as if the device is connected
to a Cellular network. This means that the updates will be allowed
based on the same settings that govern if the updates are allowed
over Cellular networks.

BUG=chromium:323010
TEST=Unit tests added to verify policy and property parsing.

Change-Id: I3a31c804465c9ed5c76b5d6156adda8e5e4e8a6d
Reviewed-on: https://chromium-review.googlesource.com/189524
Tested-by: Alex Deymo <deymo@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/connection_manager.cc b/connection_manager.cc
index 97bc1bd..8ac1079 100644
--- a/connection_manager.cc
+++ b/connection_manager.cc
@@ -111,9 +111,22 @@
   return kNetUnknown;
 }
 
-bool GetServicePathType(DBusWrapperInterface* dbus_iface,
-                        const string& path,
-                        NetworkConnectionType* out_type) {
+NetworkTethering ParseTethering(const char* tethering_str) {
+  if (!strcmp(tethering_str, shill::kTetheringNotDetectedState)) {
+    return NetworkTethering::kNotDetected;
+  } else if (!strcmp(tethering_str, shill::kTetheringSuspectedState)) {
+    return NetworkTethering::kSuspected;
+  } else if (!strcmp(tethering_str, shill::kTetheringConfirmedState)) {
+    return NetworkTethering::kConfirmed;
+  }
+  LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
+  return NetworkTethering::kUnknown;
+}
+
+bool GetServicePathProperties(DBusWrapperInterface* dbus_iface,
+                              const string& path,
+                              NetworkConnectionType* out_type,
+                              NetworkTethering* out_tethering) {
   GHashTable* hash_table = NULL;
 
   TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
@@ -121,8 +134,23 @@
                                       shill::kFlimflamServiceInterface,
                                       &hash_table));
 
+  // Populate the out_tethering.
   GValue* value = (GValue*)g_hash_table_lookup(hash_table,
-                                               shill::kTypeProperty);
+                                               shill::kTetheringProperty);
+  const char* tethering_str = NULL;
+
+  if (value != NULL)
+    tethering_str = g_value_get_string(value);
+  if (tethering_str != NULL) {
+    *out_tethering = ParseTethering(tethering_str);
+  } else {
+    // Set to Unknown if not present.
+    *out_tethering = NetworkTethering::kUnknown;
+  }
+
+  // Populate the out_type property.
+  value = (GValue*)g_hash_table_lookup(hash_table,
+                                       shill::kTypeProperty);
   const char* type_str = NULL;
   bool success = false;
   if (value != NULL && (type_str = g_value_get_string(value)) != NULL) {
@@ -151,7 +179,8 @@
 ConnectionManager::ConnectionManager(SystemState *system_state)
     :  system_state_(system_state) {}
 
-bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type) const {
+bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
+                                            NetworkTethering tethering) const {
   switch (type) {
     case kNetBluetooth:
       return false;
@@ -210,6 +239,12 @@
     }
 
     default:
+      if (tethering == NetworkTethering::kConfirmed) {
+        // Treat this connection as if it is a cellular connection.
+        LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
+                     "setting.";
+        return IsUpdateAllowedOver(kNetCellular, NetworkTethering::kUnknown);
+      }
       return true;
   }
 }
@@ -227,15 +262,33 @@
   return kValues[type];
 }
 
-bool ConnectionManager::GetConnectionType(
+const char* ConnectionManager::StringForTethering(
+    NetworkTethering tethering) const {
+  switch (tethering) {
+    case NetworkTethering::kNotDetected:
+      return shill::kTetheringNotDetectedState;
+    case NetworkTethering::kSuspected:
+      return shill::kTetheringSuspectedState;
+    case NetworkTethering::kConfirmed:
+      return shill::kTetheringConfirmedState;
+    case NetworkTethering::kUnknown:
+      return "Unknown";
+  }
+  // The program shouldn't reach this point, but the compiler isn't smart
+  // enough to infer that.
+  return "Unknown";
+}
+
+bool ConnectionManager::GetConnectionProperties(
     DBusWrapperInterface* dbus_iface,
-    NetworkConnectionType* out_type) const {
+    NetworkConnectionType* out_type,
+    NetworkTethering* out_tethering) const {
   string default_service_path;
   TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
                                               &default_service_path));
-  TEST_AND_RETURN_FALSE(GetServicePathType(dbus_iface,
-                                           default_service_path,
-                                           out_type));
+  TEST_AND_RETURN_FALSE(GetServicePathProperties(dbus_iface,
+                                                 default_service_path,
+                                                 out_type, out_tethering));
   return true;
 }