blob: 189189ae6527c62427e30d904aec7b9b24fb691a [file] [log] [blame]
// Copyright (c) 2013 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 "shill/mobile_operator.h"
#include <vector>
#include <base/stl_util.h>
#include <chromeos/dbus/service_constants.h>
#include <mobile_provider.h>
#include "shill/cellular_operator_info.h"
#include "shill/logging.h"
#include "shill/modem_info.h"
using std::string;
using std::vector;
namespace shill {
namespace {
const char kCodeKey[] = "code";
const char kCountryKey[] = "country";
const char kNameKey[] = "name";
bool CompareStringmapValue(const Stringmap &dict, const string &key,
const string &value) {
if (!ContainsKey(dict, key))
return value.empty();
return dict.find(key)->second == value;
}
const CellularOperatorInfo::CellularOperator *LookupCellularOperatorInfo(
shill::ModemInfo *modem_info, const string &code,
MobileOperator::OperatorCodeType type) {
if (type == MobileOperator::kOperatorCodeTypeMCCMNC)
return modem_info->cellular_operator_info()->
GetCellularOperatorByMCCMNC(code);
if (type == MobileOperator::kOperatorCodeTypeSID)
return modem_info->cellular_operator_info()->
GetCellularOperatorBySID(code);
return nullptr;
}
mobile_provider *LookupMobileProviderDB(
shill::ModemInfo *modem_info, const string &code, const string &name,
MobileOperator::OperatorCodeType type) {
if (type != MobileOperator::kOperatorCodeTypeMCCMNC)
return nullptr;
return mobile_provider_lookup_best_match(
modem_info->provider_db(), name.c_str(), code.c_str());
}
bool AssignDictData(const Stringmap &from, const vector<string> &keys,
Stringmap *to) {
bool contents_changed = false;
for (const string &key : keys) {
string value;
if (ContainsKey(from, key))
value = from.find(key)->second;
if (!CompareStringmapValue(*to, key, value)) {
contents_changed = true;
if (value.empty())
to->erase(key);
else
(*to)[key] = value;
}
}
return contents_changed;
}
bool AssignOperatorData(const Stringmap &from, Stringmap *to) {
vector<string> keys = { kCodeKey, kNameKey, kCountryKey };
return AssignDictData(from, keys, to);
}
bool AssignOlpData(const Stringmap &from, Stringmap *to) {
vector<string> keys = { "url", "method", "postdata" };
return AssignDictData(from, keys, to);
}
Stringmaps BuildApnListFromCellularOperatorInfoResult(
const CellularOperatorInfo::CellularOperator *info) {
Stringmaps apn_list;
for (const CellularOperatorInfo::MobileAPN *apn : info->apn_list().get()) {
Stringmap apn_dict;
if (!apn->apn.empty())
apn_dict[shill::kApnProperty] = apn->apn;
if (!apn->username.empty())
apn_dict[shill::kApnUsernameProperty] = apn->username;
if (!apn->password.empty())
apn_dict[shill::kApnPasswordProperty] = apn->password;
string lname, lang, name;
for (const CellularOperatorInfo::LocalizedName &apn_name :
apn->name_list) {
if (!apn_name.language.empty()) {
if (lname.empty()) {
lname = apn_name.name;
lang = apn_name.language;
}
} else if (name.empty()) {
name = apn_name.name;
}
}
if (!name.empty())
apn_dict[shill::kApnNameProperty] = name;
if (!lname.empty()) {
apn_dict[shill::kApnLocalizedNameProperty] = lname;
apn_dict[shill::kApnLanguageProperty] = lang;
}
apn_list.push_back(apn_dict);
}
return apn_list;
}
Stringmaps BuildApnListFromMobileProviderDbResult(mobile_provider *provider) {
Stringmaps apn_list;
for (int i = 0; i < provider->num_apns; ++i) {
Stringmap apn_dict;
mobile_apn *apn = provider->apns[i];
if (apn->value)
apn_dict[shill::kApnProperty] = apn->value;
if (apn->username)
apn_dict[shill::kApnUsernameProperty] = apn->username;
if (apn->password)
apn_dict[shill::kApnPasswordProperty] = apn->password;
const localized_name *lname = NULL;
const localized_name *name = NULL;
for (int j = 0; j < apn->num_names; ++j) {
if (apn->names[j]->lang) {
if (!lname) {
lname = apn->names[j];
}
} else if (!name) {
name = apn->names[j];
}
}
if (name) {
apn_dict[kApnNameProperty] = name->name;
}
if (lname) {
apn_dict[kApnLocalizedNameProperty] = lname->name;
apn_dict[kApnLanguageProperty] = lname->lang;
}
apn_list.push_back(apn_dict);
}
return apn_list;
}
struct LookupResult {
// |operator_data| contains the following keys:
//
// kNameKey: The name of the operator,
// kCodeKey: The operator code,
// kCountryKey: Country code,
//
// Any of the above keys may not be present in the returned dictionary, if a
// matching value was not found.
Stringmap operator_data;
bool requires_roaming;
Stringmaps apn_list;
};
// Finds the best match for the given data based on CellularOperatorInfo and
// mobile_provider_db. If |get_apns| is true, an APN list will be constructed
// if a carrier is found. If no match is found in the database, the kNameKey
// and kCodeKey keys for |operator_data| of the return value will be set to
// |operator_name| and |operator_code|, respectively.
LookupResult FindMatchingOperatorResult(shill::ModemInfo *modem_info,
const string &operator_code,
const string &operator_name,
MobileOperator::OperatorCodeType type,
bool get_apns) {
LookupResult result;
// First look up in CellularOperatorInfo.
const CellularOperatorInfo::CellularOperator *info =
LookupCellularOperatorInfo(modem_info, operator_code, type);
if (info) {
// Match found, depend on information from here.
SLOG(Cellular, 3) << "Found match for operator code " << operator_code
<< "in CellularOperatorInfo.";
result.operator_data[kCodeKey] = operator_code;
if (!operator_name.empty())
result.operator_data[kNameKey] = operator_name;
else if (!info->name_list().empty())
result.operator_data[kNameKey] = info->name_list()[0].name;
result.operator_data[kCountryKey] = info->country();
result.requires_roaming = false;
// Build APN list if requested.
if (get_apns)
result.apn_list = BuildApnListFromCellularOperatorInfoResult(info);
return result;
}
// Look up mobile_provider_db.
mobile_provider *provider = LookupMobileProviderDB(
modem_info, operator_code, operator_name, type);
if (provider) {
SLOG(Cellular, 3) << "Found match for operator code " << operator_code
<< "in mobile_provider_db.";
// mobile_provider_db look up works using both |code| and |name|. If no
// operator code was provided, use what was returned from the database.
if (!operator_code.empty())
result.operator_data[kCodeKey] = operator_code;
else if (provider->networks && provider->networks[0])
result.operator_data[kCodeKey] = provider->networks[0];
if (provider->country)
result.operator_data[kCountryKey] = provider->country;
if (!operator_name.empty())
result.operator_data[kNameKey] = operator_name;
else {
const gchar *name = mobile_provider_get_name(provider);
if (name)
result.operator_data[kNameKey] = name;
}
result.requires_roaming = provider->requires_roaming;
// Build APN list if requested.
if (get_apns)
result.apn_list = BuildApnListFromMobileProviderDbResult(provider);
return result;
}
SLOG(Cellular, 3) << "No match found for operator code "
<< operator_code << ".";
if (!operator_code.empty())
result.operator_data[kCodeKey] = operator_code;
if (!operator_name.empty())
result.operator_data[kNameKey] = operator_name;
return result;
}
} // namespace
MobileOperator::MobileOperator(ModemInfo *modem_info)
: modem_info_(modem_info),
home_provider_requires_roaming_(false) {}
void MobileOperator::AddObserver(Observer *observer) {
observers_.AddObserver(observer);
}
void MobileOperator::RemoveObserver(Observer *observer) {
observers_.RemoveObserver(observer);
}
void MobileOperator::SimOperatorInfoReceived(const string &operator_code,
const string &operator_name) {
if (operator_code.empty() && operator_name.empty()) {
// Clear the home provider.
if (!home_provider_.empty()) {
home_provider_.clear();
NotifyHomeProviderInfoChanged();
}
return;
}
LookupResult result = FindMatchingOperatorResult(
modem_info_, operator_code, operator_name,
kOperatorCodeTypeMCCMNC, false);
home_provider_requires_roaming_ = result.requires_roaming;
if (AssignOperatorData(result.operator_data, &home_provider_))
NotifyHomeProviderInfoChanged();
}
void MobileOperator::OtaOperatorInfoReceived(const string &operator_code,
const string &operator_name,
OperatorCodeType type) {
if (operator_code.empty() && operator_name.empty()) {
apn_list_.clear();
online_payment_url_template_.clear();
if (!serving_operator_.empty()) {
serving_operator_.clear();
NotifyServingOperatorInfoChanged();
}
return;
}
// Look up the operator and assign it to |serving_operator_|.
LookupResult result = FindMatchingOperatorResult(
modem_info_, operator_code, operator_name, type, true);
bool serving_operator_changed =
AssignOperatorData(result.operator_data, &serving_operator_);
// For now, always notify that APNList changed, as long as the constructed
// list is non-empty.
if (!apn_list_.empty() || !result.apn_list.empty()) {
apn_list_ = result.apn_list;
NotifyApnListChanged();
}
// Update the OLP. Notify observers if OLP changed.
const CellularService::OLP *olp_result = NULL;
if (type == kOperatorCodeTypeMCCMNC) {
olp_result = modem_info_->cellular_operator_info()->
GetOLPByMCCMNC(operator_code);
} else {
olp_result = modem_info_->cellular_operator_info()->
GetOLPBySID(operator_code);
}
if (olp_result) {
Stringmap olp_dict = olp_result->ToDict();
if (AssignOlpData(olp_dict, &online_payment_url_template_))
NotifyOnlinePaymentUrlTemplateChanged();
} else if (!online_payment_url_template_.empty()) {
online_payment_url_template_.clear();
NotifyOnlinePaymentUrlTemplateChanged();
}
if (serving_operator_changed)
NotifyServingOperatorInfoChanged();
}
void MobileOperator::NotifyHomeProviderInfoChanged() {
FOR_EACH_OBSERVER(Observer, observers_, OnHomeProviderInfoChanged(this));
}
void MobileOperator::NotifyServingOperatorInfoChanged() {
FOR_EACH_OBSERVER(Observer, observers_, OnServingOperatorInfoChanged(this));
}
void MobileOperator::NotifyApnListChanged() {
FOR_EACH_OBSERVER(Observer, observers_, OnApnListChanged(this));
}
void MobileOperator::NotifyOnlinePaymentUrlTemplateChanged() {
FOR_EACH_OBSERVER(
Observer, observers_, OnOnlinePaymentUrlTemplateChanged(this));
}
} // namespace shill