blob: 482d89cb11aec4710af9d0c2de08408abad03c13 [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/cellular_capability_universal.h"
#include <base/bind.h>
#include <base/stl_util.h>
#include <base/stringprintf.h>
#include <base/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include <mobile_provider.h>
#include <ModemManager/ModemManager.h>
#include <string>
#include <vector>
#include "shill/adaptor_interfaces.h"
#include "shill/cellular_operator_info.h"
#include "shill/cellular_service.h"
#include "shill/dbus_properties_proxy_interface.h"
#include "shill/error.h"
#include "shill/logging.h"
#include "shill/pending_activation_store.h"
#include "shill/property_accessor.h"
#include "shill/proxy_factory.h"
#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
#error "Do not include mm-modem.h"
#endif
using base::Bind;
using base::Closure;
using std::string;
using std::vector;
namespace shill {
// static
const char CellularCapabilityUniversal::kConnectPin[] = "pin";
const char CellularCapabilityUniversal::kConnectOperatorId[] = "operator-id";
const char CellularCapabilityUniversal::kConnectApn[] = "apn";
const char CellularCapabilityUniversal::kConnectIPType[] = "ip-type";
const char CellularCapabilityUniversal::kConnectUser[] = "user";
const char CellularCapabilityUniversal::kConnectPassword[] = "password";
const char CellularCapabilityUniversal::kConnectNumber[] = "number";
const char CellularCapabilityUniversal::kConnectAllowRoaming[] =
"allow-roaming";
const char CellularCapabilityUniversal::kConnectRMProtocol[] = "rm-protocol";
const int64
CellularCapabilityUniversal::kActivationRegistrationTimeoutMilliseconds =
20000;
const int64
CellularCapabilityUniversal::kDefaultScanningOrSearchingTimeoutMilliseconds =
60000;
const int64 CellularCapabilityUniversal::kEnterPinTimeoutMilliseconds = 20000;
const int64
CellularCapabilityUniversal::kRegistrationDroppedUpdateTimeoutMilliseconds =
15000;
const char CellularCapabilityUniversal::kGenericServiceNamePrefix[] =
"Mobile Network";
const char CellularCapabilityUniversal::kRootPath[] = "/";
const char CellularCapabilityUniversal::kStatusProperty[] = "status";
const char CellularCapabilityUniversal::kOperatorLongProperty[] =
"operator-long";
const char CellularCapabilityUniversal::kOperatorShortProperty[] =
"operator-short";
const char CellularCapabilityUniversal::kOperatorCodeProperty[] =
"operator-code";
const char CellularCapabilityUniversal::kOperatorAccessTechnologyProperty[] =
"access-technology";
const char CellularCapabilityUniversal::kIpConfigPropertyMethod[] = "method";
const char CellularCapabilityUniversal::kE362ModelId[] = "E362 WWAN";
const int CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds =
20000;
unsigned int CellularCapabilityUniversal::friendly_service_name_id_ = 0;
static const char kPhoneNumber[] = "*99#";
static string AccessTechnologyToString(uint32 access_technologies) {
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
return flimflam::kNetworkTechnologyLte;
if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
return flimflam::kNetworkTechnologyEvdo;
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
return flimflam::kNetworkTechnology1Xrtt;
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
return flimflam::kNetworkTechnologyHspaPlus;
if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
return flimflam::kNetworkTechnologyHspa;
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
return flimflam::kNetworkTechnologyUmts;
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
return flimflam::kNetworkTechnologyEdge;
if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
return flimflam::kNetworkTechnologyGprs;
if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
MM_MODEM_ACCESS_TECHNOLOGY_GSM))
return flimflam::kNetworkTechnologyGsm;
return "";
}
static string AccessTechnologyToTechnologyFamily(uint32 access_technologies) {
if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_LTE |
MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS |
MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
MM_MODEM_ACCESS_TECHNOLOGY_HSDPA |
MM_MODEM_ACCESS_TECHNOLOGY_UMTS |
MM_MODEM_ACCESS_TECHNOLOGY_EDGE |
MM_MODEM_ACCESS_TECHNOLOGY_GPRS |
MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
MM_MODEM_ACCESS_TECHNOLOGY_GSM))
return flimflam::kTechnologyFamilyGsm;
if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
MM_MODEM_ACCESS_TECHNOLOGY_EVDOB |
MM_MODEM_ACCESS_TECHNOLOGY_1XRTT))
return flimflam::kTechnologyFamilyCdma;
return "";
}
CellularCapabilityUniversal::CellularCapabilityUniversal(
Cellular *cellular,
ProxyFactory *proxy_factory,
ModemInfo *modem_info)
: CellularCapability(cellular, proxy_factory, modem_info),
weak_ptr_factory_(this),
registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
current_capabilities_(MM_MODEM_CAPABILITY_NONE),
access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
home_provider_(NULL),
provider_requires_roaming_(false),
resetting_(false),
scanning_supported_(false),
scanning_(false),
scanning_or_searching_(false),
scan_interval_(0),
sim_present_(false),
reset_done_(false),
scanning_or_searching_timeout_milliseconds_(
kDefaultScanningOrSearchingTimeoutMilliseconds),
activation_registration_timeout_milliseconds_(
kActivationRegistrationTimeoutMilliseconds),
registration_dropped_update_timeout_milliseconds_(
kRegistrationDroppedUpdateTimeoutMilliseconds) {
SLOG(Cellular, 2) << "Cellular capability constructed: Universal";
PropertyStore *store = cellular->mutable_store();
store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
&scanning_supported_);
store->RegisterConstString(flimflam::kEsnProperty, &esn_);
store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
&firmware_revision_);
store->RegisterConstString(flimflam::kHardwareRevisionProperty,
&hardware_revision_);
store->RegisterConstString(flimflam::kImeiProperty, &imei_);
store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
store->RegisterConstString(flimflam::kIccidProperty, &sim_identifier_);
store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
store->RegisterConstString(flimflam::kMeidProperty, &meid_);
store->RegisterConstString(flimflam::kMinProperty, &min_);
store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
store->RegisterConstString(flimflam::kSelectedNetworkProperty,
&selected_network_);
store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
&found_networks_);
store->RegisterConstBool(shill::kProviderRequiresRoamingProperty,
&provider_requires_roaming_);
store->RegisterConstBool(flimflam::kScanningProperty,
&scanning_or_searching_);
store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
HelpRegisterConstDerivedKeyValueStore(
flimflam::kSIMLockStatusProperty,
&CellularCapabilityUniversal::SimLockStatusToProperty);
store->RegisterConstString(shill::kSIMOperatorIdProperty, &operator_id_);
store->RegisterConstBool(shill::kSIMPresentProperty, &sim_present_);
store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
&apn_list_);
}
KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
Error */*error*/) {
KeyValueStore status;
string lock_type;
switch (sim_lock_status_.lock_type) {
case MM_MODEM_LOCK_SIM_PIN:
lock_type = "sim-pin";
break;
case MM_MODEM_LOCK_SIM_PUK:
lock_type = "sim-puk";
break;
default:
lock_type = "";
break;
}
status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
status.SetString(flimflam::kSIMLockTypeProperty, lock_type);
status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
sim_lock_status_.retries_left);
return status;
}
void CellularCapabilityUniversal::HelpRegisterConstDerivedKeyValueStore(
const string &name,
KeyValueStore(CellularCapabilityUniversal::*get)(Error *error)) {
cellular()->mutable_store()->RegisterDerivedKeyValueStore(
name,
KeyValueStoreAccessor(
new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
this, get, NULL)));
}
void CellularCapabilityUniversal::InitProxies() {
modem_3gpp_proxy_.reset(
proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
cellular()->dbus_owner()));
modem_proxy_.reset(
proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
cellular()->dbus_owner()));
modem_simple_proxy_.reset(
proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
cellular()->dbus_owner()));
modem_proxy_->set_state_changed_callback(
Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
weak_ptr_factory_.GetWeakPtr()));
// Do not create a SIM proxy until the device is enabled because we
// do not yet know the object path of the sim object.
// TODO(jglasgow): register callbacks
}
void CellularCapabilityUniversal::StartModem(Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
InitProxies();
// ModemManager must be in the disabled state to accept the Enable command.
Cellular::ModemState state =
static_cast<Cellular::ModemState>(modem_proxy_->State());
if (state == Cellular::kModemStateDisabled) {
EnableModem(error, callback);
} else if (!cellular()->IsUnderlyingDeviceEnabled()) {
SLOG(Cellular, 2) << "Enabling of modem deferred because state is "
<< state;
deferred_enable_modem_callback_ =
Bind(&CellularCapabilityUniversal::EnableModem,
weak_ptr_factory_.GetWeakPtr(), static_cast<Error *>(NULL),
callback);
} else {
// This request cannot be completed synchronously here because a method
// reply requires a continuation tag that is not created until the message
// handler returns, see DBus-C++ ObjectAdapter::handle_message().
cellular()->dispatcher()->PostTask(
Bind(&CellularCapabilityUniversal::Start_ModemAlreadyEnabled,
weak_ptr_factory_.GetWeakPtr(), callback));
}
}
void CellularCapabilityUniversal::EnableModem(Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
CHECK(!callback.is_null());
Error local_error(Error::kOperationInitiated);
modem_info()->metrics()->NotifyDeviceEnableStarted(
cellular()->interface_index());
modem_proxy_->Enable(
true,
&local_error,
Bind(&CellularCapabilityUniversal::Start_EnableModemCompleted,
weak_ptr_factory_.GetWeakPtr(), callback),
kTimeoutEnable);
if (local_error.IsFailure()) {
SLOG(Cellular, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
}
if (error) {
error->CopyFrom(local_error);
}
}
void CellularCapabilityUniversal::Start_ModemAlreadyEnabled(
const ResultCallback &callback) {
// Call GetProperties() here to sync up with the modem state
GetProperties();
callback.Run(Error());
}
void CellularCapabilityUniversal::Start_EnableModemCompleted(
const ResultCallback &callback, const Error &error) {
SLOG(Cellular, 2) << __func__ << ": " << error;
if (error.IsFailure()) {
callback.Run(error);
return;
}
// After modem is enabled, it should be possible to get properties
// TODO(jglasgow): handle errors from GetProperties
GetProperties();
// We expect the modem to start scanning after it has been enabled.
// Change this if this behavior is no longer the case in the future.
modem_info()->metrics()->NotifyDeviceEnableFinished(
cellular()->interface_index());
modem_info()->metrics()->NotifyDeviceScanStarted(
cellular()->interface_index());
callback.Run(error);
}
void CellularCapabilityUniversal::StopModem(Error *error,
const ResultCallback &callback) {
CHECK(!callback.is_null());
CHECK(error);
// If there is an outstanding registration change, simply ignore it since
// the service will be destroyed anyway.
if (!registration_dropped_update_callback_.IsCancelled()) {
registration_dropped_update_callback_.Cancel();
SLOG(Cellular, 2) << __func__ << " Cancelled delayed deregister.";
}
Cellular::ModemState state = cellular()->modem_state();
SLOG(Cellular, 2) << __func__ << "(" << state << ")";
if (cellular()->IsModemRegistered()) {
string all_bearers("/"); // "/" means all bearers. See Modemanager docs.
modem_simple_proxy_->Disconnect(
all_bearers,
error,
Bind(&CellularCapabilityUniversal::Stop_DisconnectCompleted,
weak_ptr_factory_.GetWeakPtr(), callback),
kTimeoutDisconnect);
if (error->IsFailure())
callback.Run(*error);
} else {
Closure task = Bind(&CellularCapabilityUniversal::Stop_Disable,
weak_ptr_factory_.GetWeakPtr(),
callback);
cellular()->dispatcher()->PostTask(task);
}
deferred_enable_modem_callback_.Reset();
}
void CellularCapabilityUniversal::Stop_DisconnectCompleted(
const ResultCallback &callback, const Error &error) {
SLOG(Cellular, 2) << __func__;
LOG_IF(ERROR, error.IsFailure()) << "Disconnect failed. Ignoring.";
Stop_Disable(callback);
}
void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
Error error;
modem_info()->metrics()->NotifyDeviceDisableStarted(
cellular()->interface_index());
modem_proxy_->Enable(
false, &error,
Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
weak_ptr_factory_.GetWeakPtr(), callback),
kTimeoutEnable);
if (error.IsFailure())
callback.Run(error);
}
void CellularCapabilityUniversal::Stop_DisableCompleted(
const ResultCallback &callback, const Error &error) {
SLOG(Cellular, 2) << __func__;
if (error.IsSuccess()) {
// The modem has been successfully disabled, but we still need to power it
// down.
Stop_PowerDown(callback);
} else {
// An error occurred; terminate the disable sequence.
callback.Run(error);
}
}
void CellularCapabilityUniversal::Stop_PowerDown(
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
Error error;
modem_proxy_->SetPowerState(
MM_MODEM_POWER_STATE_LOW,
&error,
Bind(&CellularCapabilityUniversal::Stop_PowerDownCompleted,
weak_ptr_factory_.GetWeakPtr(), callback),
kSetPowerStateTimeoutMilliseconds);
if (error.IsFailure())
// This really shouldn't happen, but if it does, report success,
// because a stop initiated power down is only called if the
// modem was successfully disabled, but the failure of this
// operation should still be propagated up as a successful disable.
Stop_PowerDownCompleted(callback, error);
}
void CellularCapabilityUniversal::Stop_PowerDownCompleted(
const ResultCallback &callback,
const Error &error) {
SLOG(Cellular, 2) << __func__;
if (error.IsFailure())
SLOG(Cellular, 2) << "Ignoring error returned by SetPowerState: " << error;
// Since the disable succeeded, if power down fails, we currently fail
// silently, i.e. we need to report the disable operation as having
// succeeded.
modem_info()->metrics()->NotifyDeviceDisableFinished(
cellular()->interface_index());
ReleaseProxies();
callback.Run(Error());
}
void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
weak_ptr_factory_.GetWeakPtr(),
callback);
modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
}
void CellularCapabilityUniversal::Disconnect(Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
// If a deferred registration loss request exists, process it.
if (!registration_dropped_update_callback_.IsCancelled()) {
registration_dropped_update_callback_.callback().Run();
DCHECK(cellular()->state() != Cellular::kStateConnected &&
cellular()->state() != Cellular::kStateLinked);
SLOG(Cellular, 1) << "Processed deferred registration loss before "
<< "disconnect request.";
}
if (bearer_path_.empty()) {
LOG(WARNING) << "In " << __func__ << "(): "
<< "Ignoring attempt to disconnect without bearer";
} else if (modem_simple_proxy_.get()) {
modem_simple_proxy_->Disconnect(bearer_path_,
error,
callback,
kTimeoutDisconnect);
}
}
void CellularCapabilityUniversal::DisconnectCleanup() {
SLOG(Cellular, 2) << __func__;
}
void CellularCapabilityUniversal::Activate(const string &carrier,
Error *error,
const ResultCallback &callback) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapabilityUniversal::OnActivationWaitForRegisterTimeout() {
SLOG(Cellular, 2) << __func__;
if (sim_identifier_.empty() ||
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_) == PendingActivationStore::kStateActivated) {
SLOG(Cellular, 2) << "Modem is already scheduled to be reset.";
return;
}
if (IsMdnValid()) {
SLOG(Cellular, 2) << "MDN is valid. Already activated.";
return;
}
if (reset_done_) {
SLOG(Cellular, 2) << "Already done with reset.";
return;
}
// Still not activated after timeout. Reset the modem.
SLOG(Cellular, 2) << "Still not registered after timeout. Reset directly "
<< "to update MDN.";
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_,
PendingActivationStore::kStatePendingTimeout);
ResetAfterActivation();
}
void CellularCapabilityUniversal::CompleteActivation(Error *error) {
SLOG(Cellular, 2) << __func__;
// Persist the ICCID as "Pending Activation".
// We're assuming that when this function gets called, |sim_identifier_| will
// be non-empty. We still check here that is non-empty, though something is
// wrong if it is empty.
if (IsMdnValid()) {
SLOG(Cellular, 2) << "Already acquired a valid MDN. Already activated.";
return;
}
if (sim_identifier_.empty()) {
SLOG(Cellular, 2) << "SIM identifier not available. Nothing to do.";
return;
}
// There should be a cellular service at this point.
if (cellular()->service().get())
cellular()->service()->SetActivationState(
flimflam::kActivationStateActivating);
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_,
PendingActivationStore::kStatePending);
activation_wait_for_registration_callback_.Reset(
Bind(&CellularCapabilityUniversal::OnActivationWaitForRegisterTimeout,
weak_ptr_factory_.GetWeakPtr()));
cellular()->dispatcher()->PostDelayedTask(
activation_wait_for_registration_callback_.callback(),
activation_registration_timeout_milliseconds_);
}
void CellularCapabilityUniversal::ResetAfterActivation() {
SLOG(Cellular, 2) << __func__;
// Here the initial call to Reset might fail in rare cases. Simply ignore.
Error error;
ResultCallback callback = Bind(
&CellularCapabilityUniversal::OnResetAfterActivationReply,
weak_ptr_factory_.GetWeakPtr());
Reset(&error, callback);
if (error.IsFailure())
SLOG(Cellular, 2) << "Failed to reset after activation.";
}
void CellularCapabilityUniversal::OnResetAfterActivationReply(
const Error &error) {
SLOG(Cellular, 2) << __func__;
if (error.IsFailure()) {
SLOG(Cellular, 2) << "Failed to reset after activation. Try again later.";
// TODO(armansito): Maybe post a delayed reset task?
return;
}
reset_done_ = true;
activation_wait_for_registration_callback_.Cancel();
UpdatePendingActivationState();
}
void CellularCapabilityUniversal::UpdatePendingActivationState() {
SLOG(Cellular, 2) << __func__;
bool registered =
registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
// If we have a valid MDN, the service is activated. Always try to remove
// the ICCID from persistence.
bool got_mdn = IsMdnValid();
if (got_mdn && !sim_identifier_.empty())
modem_info()->pending_activation_store()->RemoveEntry(
PendingActivationStore::kIdentifierICCID,
sim_identifier_);
CellularServiceRefPtr service = cellular()->service();
if (!service.get())
return;
if (service->activation_state() == flimflam::kActivationStateActivated)
// Either no service or already activated. Nothing to do.
return;
// If we have a valid MDN or we can connect to the network, then the service
// is activated.
if (got_mdn || cellular()->state() == Cellular::kStateConnected ||
cellular()->state() == Cellular::kStateLinked) {
SLOG(Cellular, 2) << "Marking service as activated.";
service->SetActivationState(flimflam::kActivationStateActivated);
return;
}
// If the ICCID is not available, the following logic can be delayed until it
// becomes available.
if (sim_identifier_.empty())
return;
PendingActivationStore::State state =
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_);
switch (state) {
case PendingActivationStore::kStatePending:
// Always mark the service as activating here, as the ICCID could have
// been unavailable earlier.
service->SetActivationState(flimflam::kActivationStateActivating);
if (reset_done_) {
SLOG(Cellular, 2) << "Post-payment activation reset complete.";
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_,
PendingActivationStore::kStateActivated);
} else if (registered) {
SLOG(Cellular, 2) << "Resetting modem for activation.";
activation_wait_for_registration_callback_.Cancel();
ResetAfterActivation();
}
break;
case PendingActivationStore::kStateActivated:
if (registered) {
// Trigger auto connect here.
SLOG(Cellular, 2) << "Modem has been reset at least once, try to "
<< "autoconnect to force MDN to update.";
service->AutoConnect();
}
break;
case PendingActivationStore::kStatePendingTimeout:
SLOG(Cellular, 2) << "Modem failed to register within timeout, but has "
<< "been reset at least once.";
if (registered) {
SLOG(Cellular, 2) << "Registered to network, marking as activated.";
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_,
PendingActivationStore::kStateActivated);
service->SetActivationState(flimflam::kActivationStateActivated);
}
break;
case PendingActivationStore::kStateUnknown:
// No entry exists for this ICCID. Nothing to do.
break;
default:
NOTREACHED();
}
}
void CellularCapabilityUniversal::ReleaseProxies() {
SLOG(Cellular, 2) << __func__;
modem_3gpp_proxy_.reset();
modem_proxy_.reset();
modem_simple_proxy_.reset();
sim_proxy_.reset();
}
void CellularCapabilityUniversal::UpdateStorageIdentifier() {
if (!cellular()->service().get())
return;
// Lookup the unique identifier assigned to the current network and base the
// service's storage identifier on it.
const string prefix = "cellular_" + cellular()->address() + "_";
string storage_id;
if (!operator_id_.empty()) {
const CellularOperatorInfo::CellularOperator *provider =
modem_info()->cellular_operator_info()->GetCellularOperatorByMCCMNC(
operator_id_);
if (provider && !provider->identifier().empty()) {
storage_id = prefix + provider->identifier();
}
}
// If the above didn't work, append IMSI, if available.
if (storage_id.empty() && !imsi_.empty()) {
storage_id = prefix + imsi_;
}
if (!storage_id.empty()) {
cellular()->service()->SetStorageIdentifier(storage_id);
}
}
void CellularCapabilityUniversal::UpdateServiceActivationState() {
if (!cellular()->service().get())
return;
bool activation_required = IsServiceActivationRequired();
string activation_state;
PendingActivationStore::State state =
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_);
if (!sim_identifier_.empty() &&
(state == PendingActivationStore::kStatePending ||
state == PendingActivationStore::kStatePendingTimeout))
activation_state = flimflam::kActivationStateActivating;
else if (activation_required)
activation_state = flimflam::kActivationStateNotActivated;
else {
activation_state = flimflam::kActivationStateActivated;
// Mark an activated service for auto-connect by default. Since data from
// the user profile will be loaded after the call to OnServiceCreated, this
// property will be corrected based on the user data at that time.
cellular()->service()->SetAutoConnect(true);
}
cellular()->service()->SetActivationState(activation_state);
// TODO(benchan): For now, assume the cellular service is activated over
// a non-cellular network if service activation is required (i.e. a
// corresponding entry is found in the cellular operator info file).
// We will need to generalize this logic when migrating CDMA support from
// cromo to ModemManager.
cellular()->service()->SetActivateOverNonCellularNetwork(activation_required);
}
void CellularCapabilityUniversal::OnServiceCreated() {
UpdateStorageIdentifier();
UpdateServiceActivationState();
UpdateScanningProperty();
UpdateServingOperator();
UpdateOLP();
// WORKAROUND:
// E362 modems on Verizon network does not properly redirect when a SIM
// runs out of credits, we need to enforce out-of-credits detection.
// TODO(thieule): Remove this workaround (crosbug.com/p/18619).
if (model_id_ == kE362ModelId)
cellular()->service()->set_enforce_out_of_credits_detection(true);
// Make sure that the network technology is set when the service gets
// created, just in case.
cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
}
// Create the list of APNs to try, in the following order:
// - last APN that resulted in a successful connection attempt on the
// current network (if any)
// - the APN, if any, that was set by the user
// - the list of APNs found in the mobile broadband provider DB for the
// home provider associated with the current SIM
// - as a last resort, attempt to connect with no APN
void CellularCapabilityUniversal::SetupApnTryList() {
apn_try_list_.clear();
DCHECK(cellular()->service().get());
const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
if (apn_info)
apn_try_list_.push_back(*apn_info);
apn_info = cellular()->service()->GetUserSpecifiedApn();
if (apn_info)
apn_try_list_.push_back(*apn_info);
apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
}
void CellularCapabilityUniversal::SetupConnectProperties(
DBusPropertiesMap *properties) {
SetupApnTryList();
FillConnectPropertyMap(properties);
}
void CellularCapabilityUniversal::FillConnectPropertyMap(
DBusPropertiesMap *properties) {
// TODO(jglasgow): Is this really needed anymore?
(*properties)[kConnectNumber].writer().append_string(
kPhoneNumber);
(*properties)[kConnectAllowRoaming].writer().append_bool(
AllowRoaming());
if (!apn_try_list_.empty()) {
// Leave the APN at the front of the list, so that it can be recorded
// if the connect attempt succeeds.
Stringmap apn_info = apn_try_list_.front();
SLOG(Cellular, 2) << __func__ << ": Using APN "
<< apn_info[flimflam::kApnProperty];
(*properties)[kConnectApn].writer().append_string(
apn_info[flimflam::kApnProperty].c_str());
if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
(*properties)[kConnectUser].writer().append_string(
apn_info[flimflam::kApnUsernameProperty].c_str());
if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
(*properties)[kConnectPassword].writer().append_string(
apn_info[flimflam::kApnPasswordProperty].c_str());
}
}
void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
const DBus::Path &path,
const Error &error) {
SLOG(Cellular, 2) << __func__ << "(" << error << ")";
CellularServiceRefPtr service = cellular()->service();
if (!service) {
// The service could have been deleted before our Connect() request
// completes if the modem was enabled and then quickly disabled.
apn_try_list_.clear();
} else if (error.IsFailure()) {
service->ClearLastGoodApn();
// The APN that was just tried (and failed) is still at the
// front of the list, about to be removed. If the list is empty
// after that, try one last time without an APN. This may succeed
// with some modems in some cases.
if (RetriableConnectError(error) && !apn_try_list_.empty()) {
apn_try_list_.pop_front();
SLOG(Cellular, 2) << "Connect failed with invalid APN, "
<< apn_try_list_.size() << " remaining APNs to try";
DBusPropertiesMap props;
FillConnectPropertyMap(&props);
Error error;
Connect(props, &error, callback);
return;
}
} else {
if (!apn_try_list_.empty()) {
service->SetLastGoodApn(apn_try_list_.front());
apn_try_list_.clear();
}
bearer_path_ = path;
}
if (!callback.is_null())
callback.Run(error);
UpdatePendingActivationState();
}
bool CellularCapabilityUniversal::AllowRoaming() {
return provider_requires_roaming_ || allow_roaming_property();
}
bool CellularCapabilityUniversal::ShouldDetectOutOfCredit() const {
return model_id_ == kE362ModelId;
}
void CellularCapabilityUniversal::GetProperties() {
SLOG(Cellular, 2) << __func__;
scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
cellular()->dbus_owner()));
DBusPropertiesMap properties(
properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
OnModemPropertiesChanged(properties, vector<string>());
properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
OnModem3GPPPropertiesChanged(properties, vector<string>());
}
// static
string CellularCapabilityUniversal::GenerateNewGenericServiceName() {
return base::StringPrintf("%s %u",
kGenericServiceNamePrefix,
friendly_service_name_id_++);
}
string CellularCapabilityUniversal::CreateFriendlyServiceName() {
SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
// If |serving_operator_| does not have an operator ID, call
// UpdateOperatorInfo() to use |operator_id_| as a fallback when appropriate.
if (serving_operator_.GetCode().empty()) {
UpdateOperatorInfo();
}
string name = serving_operator_.GetName();
string home_provider_name = cellular()->home_provider().GetName();
if (!name.empty()) {
// If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
// rules (TS 31.102 and annex A of 122.101).
if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING &&
!home_provider_name.empty()) {
return home_provider_name + " | " + name;
}
return name;
}
if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
!home_provider_name.empty()) {
return home_provider_name;
}
if (!serving_operator_.GetCode().empty()) {
return "cellular_" + serving_operator_.GetCode();
}
return GenerateNewGenericServiceName();
}
void CellularCapabilityUniversal::SetHomeProvider() {
SLOG(Cellular, 2) << __func__ << "(IMSI: " << imsi_
<< " SPN: " << spn_ << ")";
if (!modem_info()->provider_db())
return;
// MCCMNC can be determined either from IMSI or Operator Code. Use whichever
// one is available. If both were reported by the SIM, use IMSI.
const string &network_id = imsi_.empty() ? operator_id_ : imsi_;
mobile_provider *provider = mobile_provider_lookup_best_match(
modem_info()->provider_db(),
spn_.c_str(),
network_id.c_str());
if (!provider) {
SLOG(Cellular, 2) << "3GPP provider not found.";
return;
}
// Even if provider is the same as home_provider_, it is possible
// that the spn_ has changed. Run all the code below.
home_provider_ = provider;
provider_requires_roaming_ = home_provider_->requires_roaming;
Cellular::Operator oper;
// If Operator ID is available, use that as network code, otherwise
// use what was returned from the database.
if (!operator_id_.empty()) {
oper.SetCode(operator_id_);
} else if (provider->networks && provider->networks[0]) {
oper.SetCode(provider->networks[0]);
}
if (provider->country) {
oper.SetCountry(provider->country);
}
if (spn_.empty()) {
const char *name = mobile_provider_get_name(provider);
if (name) {
oper.SetName(name);
}
} else {
oper.SetName(spn_);
}
cellular()->set_home_provider(oper);
SLOG(Cellular, 2) << "Home provider: " << oper.GetCode() << ", "
<< oper.GetName() << ", " << oper.GetCountry()
<< (provider_requires_roaming_ ? ", roaming required" : "");
InitAPNList();
UpdateServiceName();
}
void CellularCapabilityUniversal::UpdateScanningProperty() {
// Set the Scanning property to true if there is a ongoing network scan
// (i.e. |scanning_| is true) or the modem is enabled but not yet registered
// to a network.
//
// TODO(benchan): As the Device DBus interface currently does not have a
// State property to indicate whether the device is being enabled, set the
// Scanning property to true when the modem is being enabled such that
// the network UI can start showing the initializing/scanning animation as
// soon as the modem is being enabled.
Cellular::ModemState modem_state = cellular()->modem_state();
bool is_activated_service_waiting_for_registration =
((modem_state == Cellular::kModemStateEnabled ||
modem_state == Cellular::kModemStateSearching) &&
!IsServiceActivationRequired());
bool new_scanning_or_searching =
modem_state == Cellular::kModemStateEnabling ||
is_activated_service_waiting_for_registration ||
scanning_;
if (new_scanning_or_searching != scanning_or_searching_) {
scanning_or_searching_ = new_scanning_or_searching;
cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
new_scanning_or_searching);
if (!scanning_or_searching_) {
SLOG(Cellular, 2) << "Initial network scan ended. Canceling timeout.";
scanning_or_searching_timeout_callback_.Cancel();
} else if (scanning_or_searching_timeout_callback_.IsCancelled()) {
SLOG(Cellular, 2) << "Initial network scan started. Starting timeout.";
scanning_or_searching_timeout_callback_.Reset(
Bind(&CellularCapabilityUniversal::OnScanningOrSearchingTimeout,
weak_ptr_factory_.GetWeakPtr()));
cellular()->dispatcher()->PostDelayedTask(
scanning_or_searching_timeout_callback_.callback(),
scanning_or_searching_timeout_milliseconds_);
}
}
}
void CellularCapabilityUniversal::OnScanningOrSearchingTimeout() {
SLOG(Cellular, 2) << "Initial network scan timed out. Changing "
<< "flimflam::kScanningProperty to |false|.";
scanning_or_searching_ = false;
cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty, false);
}
void CellularCapabilityUniversal::UpdateOLP() {
if (!modem_info()->cellular_operator_info())
return;
const CellularService::OLP *result =
modem_info()->cellular_operator_info()->GetOLPByMCCMNC(operator_id_);
if (!result)
return;
CellularService::OLP olp;
olp.CopyFrom(*result);
string post_data = olp.GetPostData();
ReplaceSubstringsAfterOffset(&post_data, 0, "${iccid}", sim_identifier_);
ReplaceSubstringsAfterOffset(&post_data, 0, "${imei}", imei_);
ReplaceSubstringsAfterOffset(&post_data, 0, "${imsi}", imsi_);
ReplaceSubstringsAfterOffset(&post_data, 0, "${mdn}", mdn_);
ReplaceSubstringsAfterOffset(&post_data, 0, "${min}", min_);
olp.SetPostData(post_data);
cellular()->service()->SetOLP(olp);
}
void CellularCapabilityUniversal::UpdateServiceName() {
if (cellular()->service()) {
cellular()->service()->SetFriendlyName(CreateFriendlyServiceName());
}
}
void CellularCapabilityUniversal::UpdateOperatorInfo() {
SLOG(Cellular, 2) << __func__;
// TODO(armansito): Use CellularOperatorInfo here instead of
// mobile_provider_db.
// Sometimes the modem fails to acquire the operator code OTA, in which case
// |serving_operator_| may not have an operator ID (sometimes due to service
// activation being required or broken modem firmware). Use |operator_id_| as
// a fallback when available. |operator_id_| is retrieved from the SIM card.
if (serving_operator_.GetCode().empty() && !operator_id_.empty()) {
SLOG(Cellular, 2) << "Assuming operator '" << operator_id_
<< "' as serving operator.";
serving_operator_.SetCode(operator_id_);
}
const string &network_id = serving_operator_.GetCode();
if (!network_id.empty()) {
SLOG(Cellular, 2) << "Looking up network id: " << network_id;
mobile_provider *provider =
mobile_provider_lookup_by_network(modem_info()->provider_db(),
network_id.c_str());
if (provider) {
if (serving_operator_.GetName().empty()) {
const char *provider_name = mobile_provider_get_name(provider);
if (provider_name && *provider_name) {
serving_operator_.SetName(provider_name);
}
}
if (provider->country && *provider->country) {
serving_operator_.SetCountry(provider->country);
}
SLOG(Cellular, 2) << "Operator name: " << serving_operator_.GetName()
<< ", country: " << serving_operator_.GetCountry();
} else {
SLOG(Cellular, 2) << "GSM provider not found.";
}
}
UpdateServingOperator();
}
void CellularCapabilityUniversal::UpdateServingOperator() {
SLOG(Cellular, 2) << __func__;
if (cellular()->service().get()) {
cellular()->service()->SetServingOperator(serving_operator_);
}
}
void CellularCapabilityUniversal::UpdateBearerPath() {
SLOG(Cellular, 2) << __func__;
DBusPathsCallback cb = Bind(&CellularCapabilityUniversal::OnListBearersReply,
weak_ptr_factory_.GetWeakPtr());
Error error;
modem_proxy_->ListBearers(&error, cb, kTimeoutDefault);
}
void CellularCapabilityUniversal::OnListBearersReply(
const std::vector<DBus::Path> &paths,
const Error &error) {
SLOG(Cellular, 2) << __func__ << "(" << error << ")";
if (error.IsFailure()) {
SLOG(Cellular, 2) << "ListBearers failed with error: " << error;
return;
}
// Look for the first active bearer and use its path as the connected
// one. Right now, we don't allow more than one active bearer.
DBus::Path new_bearer_path;
uint32 ipconfig_method(MM_BEARER_IP_METHOD_UNKNOWN);
string network_device;
for (size_t i = 0; i < paths.size(); ++i) {
const DBus::Path &path = paths[i];
scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
proxy_factory()->CreateDBusPropertiesProxy(path,
cellular()->dbus_owner()));
DBusPropertiesMap properties(
properties_proxy->GetAll(MM_DBUS_INTERFACE_BEARER));
if (properties.empty()) {
LOG(WARNING) << "Could not get properties of bearer \"" << path
<< "\". Bearer is likely gone and thus ignored.";
continue;
}
bool connected = false;
if (!DBusProperties::GetBool(
properties, MM_BEARER_PROPERTY_CONNECTED, &connected)) {
SLOG(Cellular, 2) << "Bearer does not indicate whether it is connected "
"or not. Assume it is not connected.";
continue;
}
if (!connected) {
continue;
}
SLOG(Cellular, 2) << "Found active bearer \"" << path << "\".";
CHECK(new_bearer_path.empty()) << "Found more than one active bearer.";
if (DBusProperties::GetString(
properties, MM_BEARER_PROPERTY_INTERFACE, &network_device)) {
SLOG(Cellular, 2) << "Bearer uses network interface \"" << network_device
<< "\".";
} else {
SLOG(Cellular, 2) << "Bearer does not specify network interface.";
}
// TODO(quiche): Add support for scenarios where the bearer is
// IPv6 only, or where there are conflicting configuration methods
// for IPv4 and IPv6. crbug.com/248360.
DBusPropertiesMap bearer_ip4config;
DBusProperties::GetDBusPropertiesMap(
properties, MM_BEARER_PROPERTY_IP4CONFIG, &bearer_ip4config);
if (DBusProperties::GetUint32(
bearer_ip4config, kIpConfigPropertyMethod, &ipconfig_method)) {
SLOG(Cellular, 2) << "Bearer has IPv4 config method " << ipconfig_method;
} else {
SLOG(Cellular, 2) << "Bearer does not specify IPv4 config method.";
for (const auto &i : bearer_ip4config) {
SLOG(Cellular, 5) << "Bearer IPv4 config has key \""
<< i.first
<< "\".";
}
}
new_bearer_path = path;
}
bearer_path_ = new_bearer_path;
if (new_bearer_path.empty()) {
SLOG(Cellular, 2) << "No active bearer found.";
return;
}
if (ipconfig_method == MM_BEARER_IP_METHOD_PPP) {
cellular()->StartPPP(network_device);
}
}
void CellularCapabilityUniversal::InitAPNList() {
SLOG(Cellular, 2) << __func__;
if (!home_provider_) {
return;
}
apn_list_.clear();
for (int i = 0; i < home_provider_->num_apns; ++i) {
Stringmap props;
mobile_apn *apn = home_provider_->apns[i];
if (apn->value) {
props[flimflam::kApnProperty] = apn->value;
}
if (apn->username) {
props[flimflam::kApnUsernameProperty] = apn->username;
}
if (apn->password) {
props[flimflam::kApnPasswordProperty] = apn->password;
}
// Find the first localized and non-localized name, if any.
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) {
props[flimflam::kApnNameProperty] = name->name;
}
if (lname) {
props[flimflam::kApnLocalizedNameProperty] = lname->name;
props[flimflam::kApnLanguageProperty] = lname->lang;
}
apn_list_.push_back(props);
}
if (cellular()->adaptor()) {
cellular()->adaptor()->EmitStringmapsChanged(
flimflam::kCellularApnListProperty, apn_list_);
} else {
LOG(ERROR) << "Null RPC service adaptor.";
}
}
bool CellularCapabilityUniversal::IsServiceActivationRequired() const {
if (!sim_identifier_.empty() &&
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierICCID,
sim_identifier_) != PendingActivationStore::kStateUnknown)
return false;
// If there is no online payment portal information, it's safer to assume
// the service does not require activation.
if (!modem_info()->cellular_operator_info())
return false;
const CellularService::OLP *olp =
modem_info()->cellular_operator_info()->GetOLPByMCCMNC(operator_id_);
if (!olp)
return false;
// To avoid false positives, it's safer to assume the service does not
// require activation if MDN is not set.
if (mdn_.empty())
return false;
// If MDN contains only zeros, the service requires activation.
return !IsMdnValid();
}
bool CellularCapabilityUniversal::IsMdnValid() const {
// Note that |mdn_| is normalized to contain only digits in OnMdnChanged().
for (size_t i = 0; i < mdn_.size(); ++i) {
if (mdn_[i] != '0')
return true;
}
return false;
}
// always called from an async context
void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__ << " \"" << selected_network_ << "\"";
CHECK(!callback.is_null());
Error error;
ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
weak_ptr_factory_.GetWeakPtr(), callback);
modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
if (error.IsFailure())
callback.Run(error);
}
void CellularCapabilityUniversal::RegisterOnNetwork(
const string &network_id,
Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__ << "(" << network_id << ")";
CHECK(error);
desired_network_ = network_id;
ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
weak_ptr_factory_.GetWeakPtr(), callback);
modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
}
void CellularCapabilityUniversal::OnRegisterReply(
const ResultCallback &callback,
const Error &error) {
SLOG(Cellular, 2) << __func__ << "(" << error << ")";
if (error.IsSuccess()) {
selected_network_ = desired_network_;
desired_network_.clear();
callback.Run(error);
return;
}
// If registration on the desired network failed,
// try to register on the home network.
if (!desired_network_.empty()) {
desired_network_.clear();
selected_network_.clear();
LOG(INFO) << "Couldn't register on selected network, trying home network";
Register(callback);
return;
}
callback.Run(error);
}
bool CellularCapabilityUniversal::IsRegistered() {
return IsRegisteredState(registration_state_);
}
bool CellularCapabilityUniversal::IsRegisteredState(
MMModem3gppRegistrationState state) {
return (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
}
void CellularCapabilityUniversal::SetUnregistered(bool searching) {
// If we're already in some non-registered state, don't override that
if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
registration_state_ =
(searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
}
}
void CellularCapabilityUniversal::RequirePIN(
const string &pin, bool require,
Error *error, const ResultCallback &callback) {
CHECK(error);
sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
}
void CellularCapabilityUniversal::EnterPIN(const string &pin,
Error *error,
const ResultCallback &callback) {
CHECK(error);
SLOG(Cellular, 2) << __func__;
sim_proxy_->SendPin(pin, error, callback, kEnterPinTimeoutMilliseconds);
}
void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
const string &pin,
Error *error,
const ResultCallback &callback) {
CHECK(error);
sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
}
void CellularCapabilityUniversal::ChangePIN(
const string &old_pin, const string &new_pin,
Error *error, const ResultCallback &callback) {
CHECK(error);
sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
}
void CellularCapabilityUniversal::Reset(Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
CHECK(error);
if (resetting_) {
Error::PopulateAndLog(error, Error::kInProgress, "Already resetting");
return;
}
ResultCallback cb = Bind(&CellularCapabilityUniversal::OnResetReply,
weak_ptr_factory_.GetWeakPtr(), callback);
modem_proxy_->Reset(error, cb, kTimeoutReset);
if (!error->IsFailure()) {
resetting_ = true;
}
}
void CellularCapabilityUniversal::OnResetReply(const ResultCallback &callback,
const Error &error) {
SLOG(Cellular, 2) << __func__;
resetting_ = false;
if (!callback.is_null())
callback.Run(error);
}
void CellularCapabilityUniversal::Scan(Error *error,
const ResultCallback &callback) {
SLOG(Cellular, 2) << __func__;
CHECK(error);
if (scanning_) {
Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
return;
}
DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
weak_ptr_factory_.GetWeakPtr(), callback);
modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
if (!error->IsFailure()) {
scanning_ = true;
UpdateScanningProperty();
}
}
void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
const ScanResults &results,
const Error &error) {
SLOG(Cellular, 2) << __func__;
// Error handling is weak. The current expectation is that on any
// error, found_networks_ should be cleared and a property change
// notification sent out.
//
// TODO(jglasgow): fix error handling
scanning_ = false;
UpdateScanningProperty();
found_networks_.clear();
if (!error.IsFailure()) {
for (ScanResults::const_iterator it = results.begin();
it != results.end(); ++it) {
found_networks_.push_back(ParseScanResult(*it));
}
}
cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
found_networks_);
// TODO(gmorain): This check for is_null() is a work-around because
// Cellular::Scan() passes a null callback. Instead: 1. Have Cellular::Scan()
// pass in a callback. 2. Have Cellular "own" the found_networks_ property
// 3. Have Cellular EmitStingMapsChanged() 4. Share the code between GSM and
// Universal.
if (!callback.is_null())
callback.Run(error);
}
Stringmap CellularCapabilityUniversal::ParseScanResult(
const ScanResult &result) {
/* ScanResults contain the following keys:
"status"
A MMModem3gppNetworkAvailability value representing network
availability status, given as an unsigned integer (signature "u").
This key will always be present.
"operator-long"
Long-format name of operator, given as a string value (signature
"s"). If the name is unknown, this field should not be present.
"operator-short"
Short-format name of operator, given as a string value
(signature "s"). If the name is unknown, this field should not
be present.
"operator-code"
Mobile code of the operator, given as a string value (signature
"s"). Returned in the format "MCCMNC", where MCC is the
three-digit ITU E.212 Mobile Country Code and MNC is the two- or
three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
"access-technology"
A MMModemAccessTechnology value representing the generic access
technology used by this mobile network, given as an unsigned
integer (signature "u").
*/
Stringmap parsed;
uint32 status;
if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
// numerical values are taken from 3GPP TS 27.007 Section 7.3.
static const char * const kStatusString[] = {
"unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
"available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
"current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
"forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
};
parsed[flimflam::kStatusProperty] = kStatusString[status];
}
uint32 tech; // MMModemAccessTechnology
if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
&tech)) {
parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
}
string operator_long, operator_short, operator_code;
if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
parsed[flimflam::kLongNameProperty] = operator_long;
if (DBusProperties::GetString(result, kOperatorShortProperty,
&operator_short))
parsed[flimflam::kShortNameProperty] = operator_short;
if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
parsed[flimflam::kNetworkIdProperty] = operator_code;
// If the long name is not available but the network ID is, look up the long
// name in the mobile provider database.
if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
parsed[flimflam::kLongNameProperty].empty()) &&
ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
mobile_provider *provider =
mobile_provider_lookup_by_network(
modem_info()->provider_db(),
parsed[flimflam::kNetworkIdProperty].c_str());
if (provider) {
const char *long_name = mobile_provider_get_name(provider);
if (long_name && *long_name) {
parsed[flimflam::kLongNameProperty] = long_name;
}
}
}
return parsed;
}
string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
// If we know that the modem is an E362, return LTE here to make sure that
// Chrome sees LTE as the network technology even if the actual technology is
// unknown.
// TODO(armansito): This hack will cause the UI to display LTE even if the
// modem doesn't support it at a given time. This might be problematic if we
// ever want to support switching between access technologies (e.g. falling
// back to 3G when LTE is not available).
if (model_id_ == kE362ModelId)
return flimflam::kNetworkTechnologyLte;
// Order is important. Return the highest speed technology
// TODO(jglasgow): change shill interfaces to a capability model
return AccessTechnologyToString(access_technologies_);
}
string CellularCapabilityUniversal::GetRoamingStateString() const {
switch (registration_state_) {
case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
return flimflam::kRoamingStateHome;
case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
return flimflam::kRoamingStateRoaming;
default:
break;
}
return flimflam::kRoamingStateUnknown;
}
void CellularCapabilityUniversal::GetSignalQuality() {
// TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
const DBus::Struct<unsigned int, bool> quality =
modem_proxy_->SignalQuality();
OnSignalQualityChanged(quality._1);
}
string CellularCapabilityUniversal::GetTypeString() const {
return AccessTechnologyToTechnologyFamily(access_technologies_);
}
void CellularCapabilityUniversal::OnModemPropertiesChanged(
const DBusPropertiesMap &properties,
const vector<string> &/* invalidated_properties */) {
// This solves a bootstrapping problem: If the modem is not yet
// enabled, there are no proxy objects associated with the capability
// object, so modem signals like StateChanged aren't seen. By monitoring
// changes to the State property via the ModemManager, we're able to
// get the initialization process started, which will result in the
// creation of the proxy objects.
//
// The first time we see the change to State (when the modem state
// is Unknown), we simply update the state, and rely on the Manager to
// enable the device when it is registered with the Manager. On subsequent
// changes to State, we need to explicitly enable the device ourselves.
int32 istate;
if (DBusProperties::GetInt32(properties, MM_MODEM_PROPERTY_STATE, &istate)) {
Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
OnModemStateChanged(state);
}
DBus::Path object_path_value;
if (DBusProperties::GetObjectPath(properties,
MM_MODEM_PROPERTY_SIM, &object_path_value))
OnSimPathChanged(object_path_value);
DBusPropertiesMap::const_iterator it =
properties.find(MM_MODEM_PROPERTY_SUPPORTEDCAPABILITIES);
if (it != properties.end()) {
const vector<uint32> &supported_capabilities = it->second;
OnSupportedCapabilitesChanged(supported_capabilities);
}
uint32 uint_value;
if (DBusProperties::GetUint32(properties,
MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
&uint_value))
OnModemCurrentCapabilitiesChanged(uint_value);
// not needed: MM_MODEM_PROPERTY_MAXBEARERS
// not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
string string_value;
if (DBusProperties::GetString(properties,
MM_MODEM_PROPERTY_MANUFACTURER,
&string_value))
OnModemManufacturerChanged(string_value);
if (DBusProperties::GetString(properties,
MM_MODEM_PROPERTY_MODEL,
&string_value))
OnModemModelChanged(string_value);
if (DBusProperties::GetString(properties,
MM_MODEM_PROPERTY_REVISION,
&string_value))
OnModemRevisionChanged(string_value);
// not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
// not needed: MM_MODEM_PROPERTY_DEVICE
// not needed: MM_MODEM_PROPERTY_DRIVER
// not needed: MM_MODEM_PROPERTY_PLUGIN
// not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
// Unlock required and SimLock
uint32 unlock_required; // This is really of type MMModemLock
bool lock_status_changed = false;
if (DBusProperties::GetUint32(properties,
MM_MODEM_PROPERTY_UNLOCKREQUIRED,
&unlock_required)) {
OnLockTypeChanged(static_cast<MMModemLock>(unlock_required));
lock_status_changed = true;
}
// Unlock retries
it = properties.find(MM_MODEM_PROPERTY_UNLOCKRETRIES);
if (it != properties.end()) {
LockRetryData lock_retries = it->second.operator LockRetryData();
OnLockRetriesChanged(lock_retries);
lock_status_changed = true;
}
if (lock_status_changed)
OnSimLockStatusChanged();
if (DBusProperties::GetUint32(properties,
MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
&uint_value))
OnAccessTechnologiesChanged(uint_value);
it = properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
if (it != properties.end()) {
DBus::Struct<unsigned int, bool> quality = it->second;
OnSignalQualityChanged(quality._1);
}
vector<string> numbers;
if (DBusProperties::GetStrings(properties, MM_MODEM_PROPERTY_OWNNUMBERS,
&numbers)) {
string mdn;
if (numbers.size() > 0)
mdn = numbers[0];
OnMdnChanged(mdn);
}
it = properties.find(MM_MODEM_PROPERTY_SUPPORTEDMODES);
if (it != properties.end()) {
const vector<DBus::Struct<uint32, uint32>> &mm_supported_modes = it->second;
vector<ModemModes> supported_modes;
for (const auto &modes : mm_supported_modes) {
supported_modes.push_back(
ModemModes(modes._1, static_cast<MMModemMode>(modes._2)));
}
OnSupportedModesChanged(supported_modes);
}
it = properties.find(MM_MODEM_PROPERTY_CURRENTMODES);
if (it != properties.end()) {
const DBus::Struct<uint32, uint32> &current_modes = it->second;
OnCurrentModesChanged(ModemModes(
current_modes._1, static_cast<MMModemMode>(current_modes._2)));
}
// au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
// au: MM_MODEM_PROPERTY_BANDS
}
void CellularCapabilityUniversal::OnDBusPropertiesChanged(
const string &interface,
const DBusPropertiesMap &changed_properties,
const vector<string> &invalidated_properties) {
SLOG(Cellular, 2) << __func__ << "(" << interface << ")";
if (interface == MM_DBUS_INTERFACE_MODEM) {
OnModemPropertiesChanged(changed_properties, invalidated_properties);
}
if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
}
if (interface == MM_DBUS_INTERFACE_SIM) {
OnSimPropertiesChanged(changed_properties, invalidated_properties);
}
}
bool CellularCapabilityUniversal::RetriableConnectError(
const Error &error) const {
if (error.type() == Error::kInvalidApn)
return true;
// modemmanager does not ever return kInvalidApn for E362 modems
// with 1.41 firmware. It remains to be seem if this will change
// with 3.x firmware.
if ((model_id_ == kE362ModelId) && (error.type() == Error::kOperationFailed))
return true;
return false;
}
void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
// TODO(petkov): Implement this.
NOTIMPLEMENTED();
}
bool CellularCapabilityUniversal::IsValidSimPath(const string &sim_path) const {
return !sim_path.empty() && sim_path != kRootPath;
}
string CellularCapabilityUniversal::NormalizeMdn(const string &mdn) const {
string normalized_mdn;
for (size_t i = 0; i < mdn.size(); ++i) {
if (IsAsciiDigit(mdn[i]))
normalized_mdn += mdn[i];
}
return normalized_mdn;
}
void CellularCapabilityUniversal::OnSimPathChanged(
const string &sim_path) {
if (sim_path == sim_path_)
return;
mm1::SimProxyInterface *proxy = NULL;
if (IsValidSimPath(sim_path))
proxy = proxy_factory()->CreateSimProxy(sim_path,
cellular()->dbus_owner());
sim_path_ = sim_path;
sim_proxy_.reset(proxy);
if (!IsValidSimPath(sim_path)) {
// Clear all data about the sim
imsi_ = "";
spn_ = "";
sim_present_ = false;
OnSimIdentifierChanged("");
OnOperatorIdChanged("");
} else {
sim_present_ = true;
scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
proxy_factory()->CreateDBusPropertiesProxy(sim_path,
cellular()->dbus_owner()));
// TODO(jglasgow): convert to async interface
DBusPropertiesMap properties(
properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
OnSimPropertiesChanged(properties, vector<string>());
}
}
void CellularCapabilityUniversal::OnSupportedCapabilitesChanged(
const vector<uint32> &supported_capabilities) {
supported_capabilities_ = supported_capabilities;
}
void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
uint32 current_capabilities) {
current_capabilities_ = current_capabilities;
// Only allow network scan when the modem's current capabilities support
// GSM/UMTS.
//
// TODO(benchan): We should consider having the modem plugins in ModemManager
// reporting whether network scan is supported.
scanning_supported_ =
(current_capabilities & MM_MODEM_CAPABILITY_GSM_UMTS) != 0;
if (cellular()->adaptor()) {
cellular()->adaptor()->EmitBoolChanged(
flimflam::kSupportNetworkScanProperty, scanning_supported_);
}
}
void CellularCapabilityUniversal::OnMdnChanged(
const string &mdn) {
mdn_ = NormalizeMdn(mdn);
UpdatePendingActivationState();
}
void CellularCapabilityUniversal::OnModemManufacturerChanged(
const string &manufacturer) {
manufacturer_ = manufacturer;
}
void CellularCapabilityUniversal::OnModemModelChanged(
const string &model) {
model_id_ = model;
}
void CellularCapabilityUniversal::OnModemRevisionChanged(
const string &revision) {
firmware_revision_ = revision;
}
void CellularCapabilityUniversal::OnModemStateChanged(
Cellular::ModemState state) {
Cellular::ModemState prev_modem_state = cellular()->modem_state();
bool was_enabled = cellular()->IsUnderlyingDeviceEnabled();
if (Cellular::IsEnabledModemState(state))
cellular()->set_modem_state(state);
SLOG(Cellular, 2) << __func__ << ": prev_modem_state: " << prev_modem_state
<< " was_enabled: " << was_enabled
<< " cellular state: "
<< cellular()->GetStateString(cellular()->state());
if (prev_modem_state != Cellular::kModemStateUnknown &&
prev_modem_state != Cellular::kModemStateEnabling &&
!was_enabled &&
cellular()->state() == Cellular::kStateDisabled &&
cellular()->IsUnderlyingDeviceEnabled()) {
cellular()->SetEnabled(true);
}
UpdateScanningProperty();
}
void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
uint32 access_technologies) {
if (access_technologies_ != access_technologies) {
const string old_type_string(GetTypeString());
access_technologies_ = access_technologies;
const string new_type_string(GetTypeString());
if (new_type_string != old_type_string) {
// TODO(jglasgow): address layering violation of emitting change
// signal here for a property owned by Cellular.
cellular()->adaptor()->EmitStringChanged(
flimflam::kTechnologyFamilyProperty, new_type_string);
}
if (cellular()->service().get()) {
cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
}
}
}
void CellularCapabilityUniversal::OnSupportedModesChanged(
const vector<ModemModes> &supported_modes) {
supported_modes_ = supported_modes;
}
void CellularCapabilityUniversal::OnCurrentModesChanged(
const ModemModes &current_modes) {
current_modes_ = current_modes;
}
void CellularCapabilityUniversal::OnLockRetriesChanged(
const LockRetryData &lock_retries) {
SLOG(Cellular, 2) << __func__;
// Look for the retries left for the current lock. Try the obtain the count
// that matches the current count. If no count for the current lock is
// available, report the first one in the dictionary.
LockRetryData::const_iterator it =
lock_retries.find(sim_lock_status_.lock_type);
if (it == lock_retries.end())
it = lock_retries.begin();
if (it != lock_retries.end())
sim_lock_status_.retries_left = it->second;
else
// Unknown, use 999
sim_lock_status_.retries_left = 999;
}
void CellularCapabilityUniversal::OnLockTypeChanged(
MMModemLock lock_type) {
SLOG(Cellular, 2) << __func__ << ": " << lock_type;
sim_lock_status_.lock_type = lock_type;
// If the SIM is in a locked state |sim_lock_status_.enabled| might be false.
// This is because the corresponding property 'EnabledFacilityLocks' is on
// the 3GPP interface and the 3GPP interface is not available while the Modem
// is in the 'LOCKED' state.
if (lock_type != MM_MODEM_LOCK_NONE &&
lock_type != MM_MODEM_LOCK_UNKNOWN &&
!sim_lock_status_.enabled)
sim_lock_status_.enabled = true;
}
void CellularCapabilityUniversal::OnSimLockStatusChanged() {
SLOG(Cellular, 2) << __func__;
cellular()->adaptor()->EmitKeyValueStoreChanged(
flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
// If the SIM is currently unlocked, assume that we need to refresh
// carrier information, since a locked SIM prevents shill from obtaining
// the necessary data to establish a connection later (e.g. IMSI).
if (!sim_path_.empty() &&
(sim_lock_status_.lock_type == MM_MODEM_LOCK_NONE ||
sim_lock_status_.lock_type == MM_MODEM_LOCK_UNKNOWN)) {
scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
proxy_factory()->CreateDBusPropertiesProxy(sim_path_,
cellular()->dbus_owner()));
DBusPropertiesMap properties(
properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
OnSimPropertiesChanged(properties, vector<string>());
}
}
void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
const DBusPropertiesMap &properties,
const vector<string> &/* invalidated_properties */) {
SLOG(Cellular, 2) << __func__;
string imei;
if (DBusProperties::GetString(properties,
MM_MODEM_MODEM3GPP_PROPERTY_IMEI,
&imei))
OnImeiChanged(imei);
// Handle registration state changes as a single change
string operator_code = serving_operator_.GetCode();
string operator_name = serving_operator_.GetName();
MMModem3gppRegistrationState state = registration_state_;
bool registration_changed = false;
uint32 uint_value;
if (DBusProperties::GetUint32(properties,
MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE,
&uint_value)) {
state = static_cast<MMModem3gppRegistrationState>(uint_value);
registration_changed = true;
}
if (DBusProperties::GetString(properties,
MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE,
&operator_code))
registration_changed = true;
if (DBusProperties::GetString(properties,
MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME,
&operator_name))
registration_changed = true;
if (registration_changed)
On3GPPRegistrationChanged(state, operator_code, operator_name);
uint32 locks = 0;
if (DBusProperties::GetUint32(
properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
&locks))
OnFacilityLocksChanged(locks);
}
void CellularCapabilityUniversal::OnImeiChanged(const string &imei) {
imei_ = imei;
}
void CellularCapabilityUniversal::On3GPPRegistrationChanged(
MMModem3gppRegistrationState state,
const string &operator_code,
const string &operator_name) {
SLOG(Cellular, 2) << __func__ << ": regstate=" << state
<< ", opercode=" << operator_code
<< ", opername=" << operator_name;
// While the modem is connected, if the state changed from a registered state
// to a non registered state, defer the state change by 15 seconds.
if (cellular()->modem_state() == Cellular::kModemStateConnected &&
IsRegistered() && !IsRegisteredState(state)) {
if (!registration_dropped_update_callback_.IsCancelled()) {
LOG(WARNING) << "Modem reported consecutive 3GPP registration drops. "
<< "Ignoring earlier notifications.";
registration_dropped_update_callback_.Cancel();
} else {
// This is not a repeated post. So, count this instance of delayed drop
// posted.
modem_info()->metrics()->Notify3GPPRegistrationDelayedDropPosted();
}
SLOG(Cellular, 2) << "Posted deferred registration state update";
registration_dropped_update_callback_.Reset(
Bind(&CellularCapabilityUniversal::Handle3GPPRegistrationChange,
weak_ptr_factory_.GetWeakPtr(),
state,
operator_code,
operator_name));
cellular()->dispatcher()->PostDelayedTask(
registration_dropped_update_callback_.callback(),
registration_dropped_update_timeout_milliseconds_);
} else {
if (!registration_dropped_update_callback_.IsCancelled()) {
SLOG(Cellular, 2) << "Cancelled a deferred registration state update";
registration_dropped_update_callback_.Cancel();
// If we cancelled the callback here, it means we had flaky network for a
// small duration.
modem_info()->metrics()->Notify3GPPRegistrationDelayedDropCanceled();
}
Handle3GPPRegistrationChange(state, operator_code, operator_name);
}
}
void CellularCapabilityUniversal::Handle3GPPRegistrationChange(
MMModem3gppRegistrationState updated_state,
string updated_operator_code,
string updated_operator_name) {
// A finished callback does not qualify as a canceled callback.
// We test for a canceled callback to check for outstanding callbacks.
// So, explicitly cancel the callback here.
registration_dropped_update_callback_.Cancel();
SLOG(Cellular, 2) << __func__ << ": regstate=" << updated_state
<< ", opercode=" << updated_operator_code
<< ", opername=" << updated_operator_name;
registration_state_ = updated_state;
serving_operator_.SetCode(updated_operator_code);
serving_operator_.SetName(updated_operator_name);
// Update the carrier name for |serving_operator_|.
UpdateOperatorInfo();
cellular()->HandleNewRegistrationState();
// Update the user facing name of the cellular service.
UpdateServiceName();
// If the modem registered with the network and the current ICCID is pending
// activation, then reset the modem.
UpdatePendingActivationState();
}
void CellularCapabilityUniversal::OnModemStateChangedSignal(
int32 old_state, int32 new_state, uint32 reason) {
SLOG(Cellular, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
<< reason << ")";
cellular()->OnModemStateChanged(static_cast<Cellular::ModemState>(old_state),
static_cast<Cellular::ModemState>(new_state),
reason);
UpdateScanningProperty();
if (!deferred_enable_modem_callback_.is_null() &&
(new_state == Cellular::kModemStateDisabled)) {
SLOG(Cellular, 2) << "Enabling modem after deferring";
deferred_enable_modem_callback_.Run();
deferred_enable_modem_callback_.Reset();
} else if (new_state == Cellular::kModemStateConnected) {
SLOG(Cellular, 2) << "Updating bearer path to reflect the active bearer.";
UpdateBearerPath();
}
}
void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
cellular()->HandleNewSignalQuality(quality);
}
void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32 locks) {
bool sim_enabled = !!(locks & MM_MODEM_3GPP_FACILITY_SIM);
if (sim_lock_status_.enabled != sim_enabled) {
sim_lock_status_.enabled = sim_enabled;
OnSimLockStatusChanged();
}
}
void CellularCapabilityUniversal::OnSimPropertiesChanged(
const DBusPropertiesMap &props,
const vector<string> &/* invalidated_properties */) {
SLOG(Cellular, 2) << __func__;
string value;
if (DBusProperties::GetString(props, MM_SIM_PROPERTY_SIMIDENTIFIER, &value))
OnSimIdentifierChanged(value);
if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORIDENTIFIER,
&value))
OnOperatorIdChanged(value);
if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORNAME, &value))
OnSpnChanged(value);
if (DBusProperties::GetString(props, MM_SIM_PROPERTY_IMSI, &value))
OnImsiChanged(value);
SetHomeProvider();
}
// TODO(armansito): The following methods should probably log their argument
// values. Need to learn if any of them need to be scrubbed.
void CellularCapabilityUniversal::OnImsiChanged(const std::string &imsi) {
imsi_ = imsi;
}
void CellularCapabilityUniversal::OnSpnChanged(const std::string &spn) {
spn_ = spn;
}
void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
sim_identifier_ = id;
UpdatePendingActivationState();
}
void CellularCapabilityUniversal::OnOperatorIdChanged(
const string &operator_id) {
SLOG(Cellular, 2) << "Operator ID = '" << operator_id << "'";
operator_id_ = operator_id;
}
} // namespace shill