blob: 19916d2c9a9339b5a670b809cac4a0e3661a3624 [file]
// 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 <time.h>
#include <glog/logging.h>
#include "src/policy.h"
namespace cashew {
// libcros CellularDataPlan property names
// (what we send to libcros)
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";
// Chromium OS Usage API Data Plan property names
// (what we receive from carrier usage API)
static const char *kCrosUsageDataPlanLastUpdateProperty = "lastUpdate";
static const char *kCrosUsageDataPlanNameProperty = "planName";
static const char *kCrosUsageDataPlanTypeProperty = "planType";
static const char *kCrosUsageDataPlanMaxBytesProperty = "maxBytes";
static const char *kCrosUsageDataPlanUsedBytesProperty = "usedBytes";
static const char *kCrosUsageDataPlanEndTimeProperty = "expirationTime";
static const char *kCrosUsageDataPlanStartTimeProperty = "startTime";
// Chromium OS Usage API Data Plan type values
static const char *kCrosUsageDataPlanTypeUnlimited = "UNLIMITED";
static const char *kCrosUsageDataPlanTypeMeteredFree = "METERED_FREE";
static const char *kCrosUsageDataPlanTypeMeteredPaid = "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),
local_bytes_used_(0), total_bytes_used_(data_bytes_used) {
CHECK_GE(data_bytes_max_, 0);
CHECK_GE(data_bytes_used_, 0);
}
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_;
}
Bytes DataPlan::GetLocalBytesUsed() const {
return local_bytes_used_;
}
Bytes DataPlan::GetTotalBytesUsed() const {
return total_bytes_used_;
}
void DataPlan::SetLocalBytesUsed(Bytes local_bytes_used) {
CHECK_GE(local_bytes_used, 0);
local_bytes_used_ = local_bytes_used;
total_bytes_used_ = data_bytes_used_ + local_bytes_used_;
if (total_bytes_used_ < 0) {
LOG(WARNING) << "SetLocalBytesUsed: overflow detected";
total_bytes_used_ = kint64max;
}
}
bool DataPlan::IsActive() const {
// is the plan current?
base::Time now = base::Time::Now();
if (now < start_time_ || now >= end_time_) {
return false;
}
// is the plan exhausted?
if (type_ != kTypeUnlimited && total_bytes_used_ >= data_bytes_max_) {
return false;
}
return true;
}
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::MessageIter.
plan[kCellularDataPlanName].writer().append_string(name_.c_str());
plan[kCellularDataPlanType].writer().append_string(
TypeToLibcrosString(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
if (type_ != kTypeUnlimited) {
plan[kCellularDataPlanDataBytesMax].writer().append_int64(data_bytes_max_);
}
// send our best estimate of data bytes used
// this is the baseline, if any, that we received from the carrier usage API
// plus any local traffic that we've measured since then
plan[kCellularDataPlanDataBytesUsed].writer().append_int64(total_bytes_used_);
return plan;
}
// static
DataPlan* DataPlan::FromDictionaryValue(const DictionaryValue *value,
const Policy *policy) {
CHECK_NOTNULL(value);
CHECK_NOTNULL(policy);
std::string last_update_iso8601;
if (!value->GetString(kCrosUsageDataPlanLastUpdateProperty,
&last_update_iso8601)) {
LOG(WARNING) << "FromDictionaryValue: no last update property";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: last update = " << last_update_iso8601;
base::Time last_update;
bool local = policy->ZonelessTimeStringsAreLocal();
if (!TimeFromIso8601(last_update_iso8601, &last_update, local)) {
LOG(WARNING) << "FromDictionaryValue: could not convert last update time";
return NULL;
}
std::string plan_name;
if (!value->GetString(kCrosUsageDataPlanNameProperty, &plan_name)) {
LOG(WARNING) << "FromDictionaryValue: no plan name property";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: plan name = " << plan_name;
std::string plan_type_string;
if (!value->GetString(kCrosUsageDataPlanTypeProperty, &plan_type_string)) {
LOG(WARNING) << "FromDictionaryValue: no plan type property";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: plan type string = " << plan_type_string;
Type plan_type;
if (!CrosUsageStringToType(plan_type_string, &plan_type)) {
LOG(WARNING) << "FromDictionaryValue: invalid plan type string: "
<< plan_type_string;
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: converted plan type string to type "
<< plan_type;
int64 max_bytes = 0;
// we only expect max bytes for metered plans
if (plan_type != kTypeUnlimited) {
if (!GetInt64FromDictionary(*value, kCrosUsageDataPlanMaxBytesProperty,
&max_bytes)) {
LOG(WARNING) << "FromDictionaryValue: no max bytes property";
return NULL;
}
if (max_bytes < 0) {
LOG(WARNING) << "FromDictionaryValue: max bytes is negative";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: max bytes = " << max_bytes;
}
int64 used_bytes = 0;
if (!GetInt64FromDictionary(*value, kCrosUsageDataPlanUsedBytesProperty,
&used_bytes)) {
// used bytes is required for metered plans, optional for unlimited plans
if (plan_type != kTypeUnlimited) {
LOG(WARNING) << "FromDictionaryValue: no used bytes property";
return NULL;
} else {
DLOG(INFO)
<< "FromDictionaryValue: no used bytes property (using default of 0)";
}
}
if (used_bytes < 0) {
LOG(WARNING) << "FromDictionaryValue: used bytes is negative";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: used bytes = " << used_bytes;
std::string start_time_iso8601;
if (!value->GetString(kCrosUsageDataPlanStartTimeProperty,
&start_time_iso8601)) {
LOG(WARNING) << "FromDictionaryValue: no start time property";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: start time = " << start_time_iso8601;
base::Time start_time;
if (!TimeFromIso8601(start_time_iso8601, &start_time, local)) {
LOG(WARNING) << "FromDictionaryValue: could not convert start time";
return NULL;
}
std::string end_time_iso8601;
if (!value->GetString(kCrosUsageDataPlanEndTimeProperty,
&end_time_iso8601)) {
LOG(WARNING) << "FromDictionaryValue: no end time property";
return NULL;
}
DLOG(INFO) << "FromDictionaryValue: end time = " << end_time_iso8601;
base::Time end_time;
if (!TimeFromIso8601(end_time_iso8601, &end_time, local)) {
LOG(WARNING) << "FromDictionaryValue: could not convert end time";
return NULL;
}
if (start_time > end_time) {
LOG(WARNING) << "FromDictionaryValue: start time > end time";
return NULL;
}
return new(std::nothrow) DataPlan(plan_name, plan_type, last_update,
start_time, end_time, max_bytes,
used_bytes);
}
// static
bool DataPlan::TimeFromIso8601(const std::string& time_8601,
base::Time *time_out,
bool zoneless_strings_are_local) {
CHECK_NOTNULL(time_out);
static const char *kIso8601UtcFormat = "%Y-%m-%dT%H:%M:%SZ";
static const char *kIso8601ZonelessFormat = "%Y-%m-%dT%H:%M:%S";
struct tm tm;
time_t tt;
bool tm_is_utc;
// convert from ISO 8601 string to struct tm
// first try the expected format (ISO 8601 with UTC 'Z' suffix)
// if that fails, try format with no timezone suffix
// we'll interpret this as either UTC or local time based on input flag
// TODO(vlaviano): support +/-nn:nn timezone specifier
if (strptime(time_8601.c_str(), kIso8601UtcFormat, &tm) != NULL) {
tm_is_utc = true;
} else if (strptime(time_8601.c_str(), kIso8601ZonelessFormat, &tm) != NULL) {
if (zoneless_strings_are_local) {
tm_is_utc = false;
} else {
tm_is_utc = true;
}
} else {
DLOG(WARNING) << "TimeFromIso8601: strptime failed for time string: "
<< time_8601;
return false;
}
// convert from struct tm to time_t
if (tm_is_utc) {
// NOTE: timegm interprets its input as being in UTC.
if ((tt = timegm(&tm)) == -1) {
DLOG(WARNING) << "TimeFromIso8601: timegm failed";
return false;
}
} else {
// NOTE: mktime interprets its input as being in local time.
if ((tt = mktime(&tm)) == -1) {
DLOG(WARNING) << "TimeFromIso8601: mktime failed";
return false;
}
}
// convert from time_t to base::Time
*time_out = base::Time::FromTimeT(tt);
return true;
}
// static
DataPlan* DataPlan::GetActivePlan(const DataPlanList& data_plans) {
DataPlanList::const_iterator it;
for (it = data_plans.begin(); it != data_plans.end(); ++it) {
DataPlan *plan = *it;
DCHECK(plan != NULL);
if (plan->IsActive()) {
return plan;
}
}
return NULL;
}
// private methods
const char* DataPlan::TypeToLibcrosString(DataPlan::Type type) const {
switch (type) {
case kTypeUnlimited:
return kCellularDataPlanTypeUnlimited;
case kTypeMeteredFree:
return kCellularDataPlanTypeMeteredFree;
case kTypeMeteredPaid:
return kCellularDataPlanTypeMeteredPaid;
default:
CHECK(false);
}
}
// static
bool DataPlan::CrosUsageStringToType(const std::string& type_string,
DataPlan::Type *type_out) {
CHECK_NOTNULL(type_out);
if (type_string == kCrosUsageDataPlanTypeUnlimited) {
*type_out = kTypeUnlimited;
} else if (type_string == kCrosUsageDataPlanTypeMeteredFree) {
*type_out = kTypeMeteredFree;
} else if (type_string == kCrosUsageDataPlanTypeMeteredPaid) {
*type_out = kTypeMeteredPaid;
} else {
return false;
}
return true;
}
// static
bool DataPlan::GetInt64FromDictionary(const DictionaryValue& dict,
const std::string& key,
int64* out_value) {
CHECK_NOTNULL(out_value);
int generic_int_out = 0;
if (dict.GetInteger(key, &generic_int_out)) {
*out_value = generic_int_out;
return true;
}
if (dict.GetInteger64(key, out_value)) {
return true;
}
return false;
}
} // namespace cashew