blob: f5d7630815d3fae38594705552d4ba0e9c746efd [file] [log] [blame]
// Copyright (c) 2012 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.h"
#include <base/bind.h>
#include <chromeos/dbus/service_constants.h>
#include "shill/cellular.h"
#include "shill/error.h"
#include "shill/property_accessor.h"
#include "shill/proxy_factory.h"
using base::Bind;
using base::Callback;
using base::Closure;
using std::string;
namespace shill {
const char CellularCapability::kConnectPropertyApn[] = "apn";
const char CellularCapability::kConnectPropertyApnUsername[] = "username";
const char CellularCapability::kConnectPropertyApnPassword[] = "password";
const char CellularCapability::kConnectPropertyHomeOnly[] = "home_only";
const char CellularCapability::kConnectPropertyPhoneNumber[] = "number";
const char CellularCapability::kPropertyIMSI[] = "imsi";
// All timeout values are in milliseconds
const int CellularCapability::kTimeoutActivate = 120000;
const int CellularCapability::kTimeoutConnect = 45000;
const int CellularCapability::kTimeoutDefault = 5000;
const int CellularCapability::kTimeoutEnable = 15000;
const int CellularCapability::kTimeoutRegister = 90000;
const int CellularCapability::kTimeoutScan = 120000;
CellularCapability::CellularCapability(Cellular *cellular,
ProxyFactory *proxy_factory)
: allow_roaming_(false),
scanning_supported_(false),
cellular_(cellular),
proxy_factory_(proxy_factory),
weak_ptr_factory_(this) {
PropertyStore *store = cellular->mutable_store();
store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
HelpRegisterDerivedBool(flimflam::kCellularAllowRoamingProperty,
&CellularCapability::GetAllowRoaming,
&CellularCapability::SetAllowRoaming);
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::kManufacturerProperty, &manufacturer_);
store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
store->RegisterConstString(flimflam::kMeidProperty, &meid_);
store->RegisterConstString(flimflam::kMinProperty, &min_);
store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
}
CellularCapability::~CellularCapability() {}
void CellularCapability::HelpRegisterDerivedBool(
const string &name,
bool(CellularCapability::*get)(Error *error),
void(CellularCapability::*set)(const bool &value, Error *error)) {
cellular()->mutable_store()->RegisterDerivedBool(
name,
BoolAccessor(
new CustomAccessor<CellularCapability, bool>(this, get, set)));
}
void CellularCapability::SetAllowRoaming(const bool &value, Error */*error*/) {
VLOG(2) << __func__ << "(" << allow_roaming_ << "->" << value << ")";
if (allow_roaming_ == value) {
return;
}
allow_roaming_ = value;
if (!value && GetRoamingStateString() == flimflam::kRoamingStateRoaming) {
Error error;
cellular()->Disconnect(&error);
}
cellular()->adaptor()->EmitBoolChanged(
flimflam::kCellularAllowRoamingProperty, value);
}
void CellularCapability::RunNextStep(CellularTaskList *tasks) {
CHECK(!tasks->empty());
VLOG(2) << __func__ << ": " << tasks->size() << " remaining tasks";
Closure task = (*tasks)[0];
tasks->erase(tasks->begin());
cellular()->dispatcher()->PostTask(task);
}
void CellularCapability::StepCompletedCallback(
const ResultCallback &callback,
bool ignore_error,
CellularTaskList *tasks,
const Error &error) {
if ((ignore_error || error.IsSuccess()) && !tasks->empty()) {
RunNextStep(tasks);
return;
}
delete tasks;
callback.Run(error);
}
void CellularCapability::InitProxies() {
VLOG(2) << __func__;
proxy_.reset(proxy_factory()->CreateModemProxy(
cellular()->dbus_path(), cellular()->dbus_owner()));
simple_proxy_.reset(proxy_factory()->CreateModemSimpleProxy(
cellular()->dbus_path(), cellular()->dbus_owner()));
proxy_->set_state_changed_callback(
Bind(&CellularCapability::OnModemStateChangedSignal,
weak_ptr_factory_.GetWeakPtr()));
}
void CellularCapability::ReleaseProxies() {
VLOG(2) << __func__;
proxy_.reset();
simple_proxy_.reset();
}
void CellularCapability::FinishEnable(const ResultCallback &callback) {
callback.Run(Error());
GetRegistrationState();
GetSignalQuality();
}
void CellularCapability::FinishDisable(const ResultCallback &callback) {
ReleaseProxies();
callback.Run(Error());
}
void CellularCapability::OnUnsupportedOperation(
const char *operation,
Error *error) {
string message("The ");
message.append(operation).append(" operation is not supported.");
Error::PopulateAndLog(error, Error::kNotSupported, message);
}
// always called from an async context
void CellularCapability::EnableModem(const ResultCallback &callback) {
VLOG(2) << __func__;
CHECK(!callback.is_null());
Error error;
proxy_->Enable(true, &error, callback, kTimeoutEnable);
if (error.IsFailure())
callback.Run(error);
}
// always called from an async context
void CellularCapability::DisableModem(const ResultCallback &callback) {
VLOG(2) << __func__;
CHECK(!callback.is_null());
Error error;
proxy_->Enable(false, &error, callback, kTimeoutDefault);
if (error.IsFailure())
callback.Run(error);
}
// always called from an async context
void CellularCapability::GetModemStatus(const ResultCallback &callback) {
VLOG(2) << __func__;
CHECK(!callback.is_null());
DBusPropertyMapCallback cb = Bind(&CellularCapability::OnGetModemStatusReply,
weak_ptr_factory_.GetWeakPtr(), callback);
Error error;
simple_proxy_->GetModemStatus(&error, cb, kTimeoutDefault);
if (error.IsFailure())
callback.Run(error);
}
// always called from an async context
void CellularCapability::GetModemInfo(const ResultCallback &callback) {
VLOG(2) << __func__;
CHECK(!callback.is_null());
ModemInfoCallback cb = Bind(&CellularCapability::OnGetModemInfoReply,
weak_ptr_factory_.GetWeakPtr(), callback);
Error error;
proxy_->GetModemInfo(&error, cb, kTimeoutDefault);
if (error.IsFailure())
callback.Run(error);
}
void CellularCapability::StopModem(Error *error,
const ResultCallback &callback) {
VLOG(2) << __func__;
CellularTaskList *tasks = new CellularTaskList();
ResultCallback cb =
Bind(&CellularCapability::StepCompletedCallback,
weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
ResultCallback cb_ignore_error =
Bind(&CellularCapability::StepCompletedCallback,
weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
tasks->push_back(Bind(&CellularCapability::Disconnect,
weak_ptr_factory_.GetWeakPtr(),
static_cast<Error *>(NULL), cb_ignore_error));
tasks->push_back(Bind(&CellularCapability::DisableModem,
weak_ptr_factory_.GetWeakPtr(), cb));
tasks->push_back(Bind(&CellularCapability::FinishDisable,
weak_ptr_factory_.GetWeakPtr(), cb));
RunNextStep(tasks);
}
void CellularCapability::Connect(const DBusPropertiesMap &properties,
Error *error,
const ResultCallback &callback) {
VLOG(2) << __func__;
ResultCallback cb = Bind(&CellularCapability::OnConnectReply,
weak_ptr_factory_.GetWeakPtr(),
callback);
simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
}
void CellularCapability::Disconnect(Error *error,
const ResultCallback &callback) {
VLOG(2) << __func__;
ResultCallback cb = Bind(&CellularCapability::OnDisconnectReply,
weak_ptr_factory_.GetWeakPtr(),
callback);
proxy_->Disconnect(error, cb, kTimeoutDefault);
}
void CellularCapability::Activate(const string &/*carrier*/,
Error *error,
const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::RegisterOnNetwork(
const string &/*network_id*/,
Error *error, const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::RequirePIN(const std::string &/*pin*/,
bool /*require*/,
Error *error,
const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::EnterPIN(const string &/*pin*/,
Error *error,
const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::UnblockPIN(const string &/*unblock_code*/,
const string &/*pin*/,
Error *error,
const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::ChangePIN(const string &/*old_pin*/,
const string &/*new_pin*/,
Error *error,
const ResultCallback &/*callback*/) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::Scan(Error *error,
const ResultCallback &callback) {
OnUnsupportedOperation(__func__, error);
}
void CellularCapability::OnGetModemStatusReply(
const ResultCallback &callback,
const DBusPropertiesMap &props,
const Error &error) {
VLOG(2) << __func__ << " " << props.size() << " props. error " << error;
if (error.IsSuccess()) {
DBusProperties::GetString(props, "carrier", &carrier_);
DBusProperties::GetString(props, "meid", &meid_);
DBusProperties::GetString(props, "imei", &imei_);
DBusProperties::GetString(props, kPropertyIMSI, &imsi_);
DBusProperties::GetString(props, "esn", &esn_);
DBusProperties::GetString(props, "mdn", &mdn_);
DBusProperties::GetString(props, "min", &min_);
DBusProperties::GetString(props, "firmware_revision", &firmware_revision_);
uint32 state;
if (DBusProperties::GetUint32(props, "state", &state))
cellular()->set_modem_state(static_cast<Cellular::ModemState>(state));
UpdateStatus(props);
}
callback.Run(error);
}
void CellularCapability::OnGetModemInfoReply(
const ResultCallback &callback,
const ModemHardwareInfo &info,
const Error &error) {
VLOG(2) << __func__ << "(" << error << ")";
if (error.IsSuccess()) {
manufacturer_ = info._1;
model_id_ = info._2;
hardware_revision_ = info._3;
VLOG(2) << __func__ << ": " << info._1 << ", " << info._2 << ", "
<< info._3;
}
callback.Run(error);
}
// TODO(ers): use the supplied callback when Connect is fully asynchronous
void CellularCapability::OnConnectReply(const ResultCallback &callback,
const Error &error) {
VLOG(2) << __func__ << "(" << error << ")";
if (error.IsSuccess())
cellular()->OnConnected();
else
cellular()->OnConnectFailed(error);
if (!callback.is_null())
callback.Run(error);
}
// TODO(ers): use the supplied callback when Disonnect is fully asynchronous
void CellularCapability::OnDisconnectReply(const ResultCallback &callback,
const Error &error) {
VLOG(2) << __func__ << "(" << error << ")";
if (error.IsSuccess())
cellular()->OnDisconnected();
else
cellular()->OnDisconnectFailed();
if (!callback.is_null())
callback.Run(error);
}
void CellularCapability::OnModemStateChangedSignal(
uint32 old_state, uint32 new_state, uint32 reason) {
VLOG(2) << __func__ << "(" << old_state << ", " << new_state << ", "
<< reason << ")";
// TODO(petkov): Complete this (crosbug.com/19662)
#if 0
modem_state_ = static_cast<ModemState>(new_state);
if (old_state == new_state) {
return;
}
switch (new_state) {
case kModemStateEnabled:
if (old_state == kModemStateDisabled ||
old_state == kModemStateEnabling) {
Start();
}
// TODO(petkov): Handle the case when the state is downgraded to Enabled.
break;
default:
break;
}
#endif
}
} // namespace shill