shill: Device: Add receive / transmit byte count properties

Create "RecieveByteCount" and "TransmitByteCount" properties for
Devices, which increment persistently across shill instances.  Also
create a "ResetByteCount" method on Devices to set these counters
back to zero.

BUG=chromium-os:31584
TEST=New unit tests; list-devices on a real machine.  Restart shill,
and ensure byte counts are persisted to disk.  Write a "reset-counters"
test script and ensure that this resets the counters and the on-disk
values are set to 0 as well.

Change-Id: I8d285310d153e1e219ef523528b575e2c600de01
Reviewed-on: https://gerrit.chromium.org/gerrit/27300
Reviewed-by: Ben Chan <benchan@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/device.cc b/device.cc
index 9eda4df..db0616f 100644
--- a/device.cc
+++ b/device.cc
@@ -62,12 +62,14 @@
 const char Device::kIPFlagReversePathFilterEnabled[] = "1";
 // static
 const char Device::kIPFlagReversePathFilterLooseMode[] = "2";
-
 // static
 const char Device::kStoragePowered[] = "Powered";
-
 // static
 const char Device::kStorageIPConfigs[] = "IPConfigs";
+// static
+const char Device::kStorageReceiveByteCount[] = "ReceiveByteCount";
+// static
+const char Device::kStorageTransmitByteCount[] = "TransmitByteCount";
 
 Device::Device(ControlInterface *control_interface,
                EventDispatcher *dispatcher,
@@ -96,6 +98,8 @@
                                      weak_ptr_factory_.GetWeakPtr())),
       technology_(technology),
       portal_attempts_to_online_(0),
+      receive_byte_offset_(0),
+      transmit_byte_offset_(0),
       dhcp_provider_(DHCPProvider::GetInstance()),
       rtnl_handler_(RTNLHandler::GetInstance()) {
   store_.RegisterConstString(flimflam::kAddressProperty, &hardware_address_);
@@ -140,6 +144,16 @@
 
   // flimflam::kScanningProperty: Registered in WiFi, Cellular
   // flimflam::kScanIntervalProperty: Registered in WiFi, Cellular
+
+  if (manager_ && manager_->device_info()) {  // Unit tests may not have these.
+    manager_->device_info()->GetByteCounts(
+        interface_index_, &receive_byte_offset_, &transmit_byte_offset_);
+    HelpRegisterConstDerivedUint64(shill::kReceiveByteCountProperty,
+                                   &Device::GetReceiveByteCount);
+    HelpRegisterConstDerivedUint64(shill::kTransmitByteCountProperty,
+                                   &Device::GetTransmitByteCount);
+  }
+
   LOG(INFO) << "Device created: " << link_name_
             << " index " << interface_index_;
 }
@@ -267,7 +281,23 @@
   }
   enabled_persistent_ = true;
   storage->GetBool(id, kStoragePowered, &enabled_persistent_);
-  // TODO(cmasone): What does it mean to load an IPConfig identifier??
+  uint64 rx_byte_count = 0, tx_byte_count = 0;
+
+  manager_->device_info()->GetByteCounts(
+      interface_index_, &rx_byte_count, &tx_byte_count);
+  // If there is a byte-count present in the profile, the return value
+  // of Device::Get*ByteCount() should be the this stored value plus
+  // whatever additional bytes we receive since time-of-load.  We
+  // accomplish this by the subtractions below, which can validly
+  // roll over "negative" in the subtractions below and in Get*ByteCount.
+  uint64 profile_byte_count;
+  if (storage->GetUint64(id, kStorageReceiveByteCount, &profile_byte_count)) {
+    receive_byte_offset_ = rx_byte_count - profile_byte_count;
+  }
+  if (storage->GetUint64(id, kStorageTransmitByteCount, &profile_byte_count)) {
+    transmit_byte_offset_ = tx_byte_count - profile_byte_count;
+  }
+
   return true;
 }
 
@@ -283,6 +313,8 @@
     ipconfig_->Save(storage, suffix);
     storage->SetString(id, kStorageIPConfigs, SerializeIPConfigs(suffix));
   }
+  storage->SetUint64(id, kStorageReceiveByteCount, GetReceiveByteCount(NULL));
+  storage->SetUint64(id, kStorageTransmitByteCount, GetTransmitByteCount(NULL));
   return true;
 }
 
@@ -346,6 +378,15 @@
           new CustomAccessor<Device, RpcIdentifiers>(this, get, NULL)));
 }
 
+void Device::HelpRegisterConstDerivedUint64(
+    const string &name,
+    uint64(Device::*get)(Error *)) {
+  store_.RegisterDerivedUint64(
+      name,
+      Uint64Accessor(
+          new CustomAccessor<Device, uint64>(this, get, NULL)));
+}
+
 void Device::ConfigureStaticIPTask() {
   SLOG(Device, 2) << __func__ << " selected_service " << selected_service_.get()
                   << " ipconfig " << ipconfig_.get();
@@ -514,6 +555,12 @@
   return true;
 }
 
+void Device::ResetByteCounters() {
+  manager_->device_info()->GetByteCounts(
+      interface_index_, &receive_byte_offset_, &transmit_byte_offset_);
+  manager_->UpdateDevice(this);
+}
+
 bool Device::RestartPortalDetection() {
   StopPortalDetection();
   return StartPortalDetection();
@@ -701,6 +748,20 @@
   return adaptor_->GetRpcConnectionIdentifier();
 }
 
+uint64 Device::GetReceiveByteCount(Error */*error*/) {
+  uint64 rx_byte_count = 0, tx_byte_count = 0;
+  manager_->device_info()->GetByteCounts(
+      interface_index_, &rx_byte_count, &tx_byte_count);
+  return rx_byte_count - receive_byte_offset_;
+}
+
+uint64 Device::GetTransmitByteCount(Error */*error*/) {
+  uint64 rx_byte_count = 0, tx_byte_count = 0;
+  manager_->device_info()->GetByteCounts(
+      interface_index_, &rx_byte_count, &tx_byte_count);
+  return tx_byte_count - transmit_byte_offset_;
+}
+
 bool Device::IsUnderlyingDeviceEnabled() const {
   return false;
 }