1// 2// Copyright (C) 2012 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "shill/cellular/cellular_capability_cdma.h" 18 19#include <string> 20#include <vector> 21 22#include <base/bind.h> 23#include <base/strings/stringprintf.h> 24#include <base/strings/string_util.h> 25#if defined(__ANDROID__) 26#include <dbus/service_constants.h> 27#else 28#include <chromeos/dbus/service_constants.h> 29#endif // __ANDROID__ 30#include <mm/mm-modem.h> 31 32#include "shill/cellular/cellular.h" 33#include "shill/cellular/cellular_service.h" 34#include "shill/control_interface.h" 35#include "shill/logging.h" 36 37using base::Bind; 38using std::string; 39using std::vector; 40 41namespace shill { 42 43namespace Logging { 44static auto kModuleLogScope = ScopeLogger::kCellular; 45static string ObjectID(CellularCapabilityCDMA* c) { 46 return c->cellular()->GetRpcIdentifier(); 47} 48} 49 50// static 51const char CellularCapabilityCDMA::kPhoneNumber[] = "#777"; 52 53CellularCapabilityCDMA::CellularCapabilityCDMA( 54 Cellular* cellular, 55 ControlInterface* control_interface, 56 ModemInfo* modem_info) 57 : CellularCapabilityClassic(cellular, control_interface, modem_info), 58 weak_ptr_factory_(this), 59 activation_starting_(false), 60 activation_state_(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED), 61 registration_state_evdo_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN), 62 registration_state_1x_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { 63 SLOG(this, 2) << "Cellular capability constructed: CDMA"; 64} 65 66CellularCapabilityCDMA::~CellularCapabilityCDMA() {} 67 68void CellularCapabilityCDMA::InitProxies() { 69 CellularCapabilityClassic::InitProxies(); 70 proxy_.reset(control_interface()->CreateModemCDMAProxy( 71 cellular()->dbus_path(), cellular()->dbus_service())); 72 proxy_->set_signal_quality_callback( 73 Bind(&CellularCapabilityCDMA::OnSignalQualitySignal, 74 weak_ptr_factory_.GetWeakPtr())); 75 proxy_->set_activation_state_callback( 76 Bind(&CellularCapabilityCDMA::OnActivationStateChangedSignal, 77 weak_ptr_factory_.GetWeakPtr())); 78 proxy_->set_registration_state_callback( 79 Bind(&CellularCapabilityCDMA::OnRegistrationStateChangedSignal, 80 weak_ptr_factory_.GetWeakPtr())); 81} 82 83string CellularCapabilityCDMA::GetTypeString() const { 84 return kTechnologyFamilyCdma; 85} 86 87void CellularCapabilityCDMA::StartModem(Error* error, 88 const ResultCallback& callback) { 89 SLOG(this, 2) << __func__; 90 InitProxies(); 91 92 CellularTaskList* tasks = new CellularTaskList(); 93 ResultCallback cb = 94 Bind(&CellularCapabilityCDMA::StepCompletedCallback, 95 weak_ptr_factory_.GetWeakPtr(), callback, false, tasks); 96 if (!cellular()->IsUnderlyingDeviceEnabled()) 97 tasks->push_back(Bind(&CellularCapabilityCDMA::EnableModem, 98 weak_ptr_factory_.GetWeakPtr(), cb)); 99 tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemStatus, 100 weak_ptr_factory_.GetWeakPtr(), cb)); 101 tasks->push_back(Bind(&CellularCapabilityCDMA::GetMEID, 102 weak_ptr_factory_.GetWeakPtr(), cb)); 103 tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemInfo, 104 weak_ptr_factory_.GetWeakPtr(), cb)); 105 tasks->push_back(Bind(&CellularCapabilityCDMA::FinishEnable, 106 weak_ptr_factory_.GetWeakPtr(), cb)); 107 108 RunNextStep(tasks); 109} 110 111void CellularCapabilityCDMA::ReleaseProxies() { 112 CellularCapabilityClassic::ReleaseProxies(); 113 proxy_.reset(); 114} 115 116bool CellularCapabilityCDMA::AreProxiesInitialized() const { 117 return (CellularCapabilityClassic::AreProxiesInitialized() && proxy_.get()); 118} 119 120bool CellularCapabilityCDMA::AllowRoaming() { 121 return allow_roaming_property(); 122} 123 124 125void CellularCapabilityCDMA::OnServiceCreated() { 126 SLOG(this, 2) << __func__; 127 cellular()->service()->SetUsageURL(usage_url_); 128 cellular()->service()->SetActivationType( 129 CellularService::kActivationTypeOTASP); 130 HandleNewActivationState(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR); 131} 132 133void CellularCapabilityCDMA::UpdateStatus(const KeyValueStore& properties) { 134 string carrier; 135 if (properties.ContainsUint("activation_state")) { 136 activation_state_ = properties.GetUint("activation_state"); 137 } 138 // TODO(petkov): For now, get the payment and usage URLs from ModemManager to 139 // match flimflam. In the future, get these from an alternative source (e.g., 140 // database, carrier-specific properties, etc.). 141 UpdateOnlinePortal(properties); 142 if (properties.ContainsUint("prl_version")) 143 cellular()->set_prl_version(properties.GetUint("prl_version")); 144} 145 146void CellularCapabilityCDMA::UpdateServiceOLP() { 147 SLOG(this, 3) << __func__; 148 // All OLP changes are routed up to the Home Provider. 149 if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown()) { 150 return; 151 } 152 153 const vector<MobileOperatorInfo::OnlinePortal>& olp_list = 154 cellular()->home_provider_info()->olp_list(); 155 if (olp_list.empty()) { 156 return; 157 } 158 159 if (olp_list.size() > 1) { 160 SLOG(this, 1) << "Found multiple online portals. Choosing the first."; 161 } 162 cellular()->service()->SetOLP(olp_list[0].url, 163 olp_list[0].method, 164 olp_list[0].post_data); 165} 166 167void CellularCapabilityCDMA::SetupConnectProperties( 168 KeyValueStore* properties) { 169 properties->SetString(kConnectPropertyPhoneNumber, kPhoneNumber); 170} 171 172void CellularCapabilityCDMA::Activate(const string& carrier, 173 Error* error, 174 const ResultCallback& callback) { 175 SLOG(this, 2) << __func__ << "(" << carrier << ")"; 176 // We're going to trigger something which leads to an activation. 177 activation_starting_ = true; 178 if (cellular()->state() == Cellular::kStateEnabled || 179 cellular()->state() == Cellular::kStateRegistered) { 180 ActivationResultCallback activation_callback = 181 Bind(&CellularCapabilityCDMA::OnActivateReply, 182 weak_ptr_factory_.GetWeakPtr(), 183 callback); 184 proxy_->Activate(carrier, error, activation_callback, kTimeoutActivate); 185 } else if (cellular()->state() == Cellular::kStateConnected || 186 cellular()->state() == Cellular::kStateLinked) { 187 pending_activation_callback_ = callback; 188 pending_activation_carrier_ = carrier; 189 cellular()->Disconnect(error, __func__); 190 } else { 191 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, 192 "Unable to activate in " + 193 Cellular::GetStateString(cellular()->state())); 194 activation_starting_ = false; 195 } 196} 197 198void CellularCapabilityCDMA::HandleNewActivationState(uint32_t error) { 199 SLOG(this, 2) << __func__ << "(" << error << ")"; 200 if (!cellular()->service().get()) { 201 LOG(ERROR) << "In " << __func__ << "(): service is null."; 202 return; 203 } 204 cellular()->service()->SetActivationState( 205 GetActivationStateString(activation_state_)); 206 cellular()->service()->set_error(GetActivationErrorString(error)); 207} 208 209void CellularCapabilityCDMA::DisconnectCleanup() { 210 CellularCapabilityClassic::DisconnectCleanup(); 211 if (pending_activation_callback_.is_null()) { 212 return; 213 } 214 if (cellular()->state() == Cellular::kStateEnabled || 215 cellular()->state() == Cellular::kStateRegistered) { 216 Error ignored_error; 217 Activate(pending_activation_carrier_, 218 &ignored_error, 219 pending_activation_callback_); 220 } else { 221 Error error; 222 Error::PopulateAndLog( 223 FROM_HERE, 224 &error, 225 Error::kOperationFailed, 226 "Tried to disconnect before activating cellular service and failed"); 227 HandleNewActivationState(MM_MODEM_CDMA_ACTIVATION_ERROR_UNKNOWN); 228 activation_starting_ = false; 229 pending_activation_callback_.Run(error); 230 } 231 pending_activation_callback_.Reset(); 232 pending_activation_carrier_.clear(); 233} 234 235// static 236string CellularCapabilityCDMA::GetActivationStateString(uint32_t state) { 237 switch (state) { 238 case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED: 239 return kActivationStateActivated; 240 case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING: 241 return kActivationStateActivating; 242 case MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED: 243 return kActivationStateNotActivated; 244 case MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED: 245 return kActivationStatePartiallyActivated; 246 default: 247 return kActivationStateUnknown; 248 } 249} 250 251// static 252string CellularCapabilityCDMA::GetActivationErrorString(uint32_t error) { 253 switch (error) { 254 case MM_MODEM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE: 255 return kErrorNeedEvdo; 256 case MM_MODEM_CDMA_ACTIVATION_ERROR_ROAMING: 257 return kErrorNeedHomeNetwork; 258 case MM_MODEM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT: 259 case MM_MODEM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED: 260 case MM_MODEM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED: 261 return kErrorOtaspFailed; 262 case MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR: 263 return ""; 264 case MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL: 265 default: 266 return kErrorActivationFailed; 267 } 268} 269 270void CellularCapabilityCDMA::GetMEID(const ResultCallback& callback) { 271 SLOG(this, 2) << __func__; 272 if (cellular()->meid().empty()) { 273 // TODO(petkov): Switch to asynchronous calls (crbug.com/200687). 274 cellular()->set_meid(proxy_->MEID()); 275 SLOG(this, 2) << "MEID: " << cellular()->meid(); 276 } 277 callback.Run(Error()); 278} 279 280void CellularCapabilityCDMA::GetProperties(const ResultCallback& callback) { 281 SLOG(this, 2) << __func__; 282 // No properties. 283 callback.Run(Error()); 284} 285 286bool CellularCapabilityCDMA::IsActivating() const { 287 return activation_starting_ || 288 activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING; 289} 290 291bool CellularCapabilityCDMA::IsRegistered() const { 292 return registration_state_evdo_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN || 293 registration_state_1x_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; 294} 295 296void CellularCapabilityCDMA::SetUnregistered(bool searching) { 297 registration_state_evdo_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; 298 registration_state_1x_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; 299} 300 301string CellularCapabilityCDMA::GetNetworkTechnologyString() const { 302 if (registration_state_evdo_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { 303 return kNetworkTechnologyEvdo; 304 } 305 if (registration_state_1x_ != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { 306 return kNetworkTechnology1Xrtt; 307 } 308 return ""; 309} 310 311string CellularCapabilityCDMA::GetRoamingStateString() const { 312 uint32_t state = registration_state_evdo_; 313 if (state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) { 314 state = registration_state_1x_; 315 } 316 switch (state) { 317 case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN: 318 case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED: 319 break; 320 case MM_MODEM_CDMA_REGISTRATION_STATE_HOME: 321 return kRoamingStateHome; 322 case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING: 323 return kRoamingStateRoaming; 324 default: 325 NOTREACHED(); 326 } 327 return kRoamingStateUnknown; 328} 329 330void CellularCapabilityCDMA::GetSignalQuality() { 331 SLOG(this, 2) << __func__; 332 SignalQualityCallback callback = 333 Bind(&CellularCapabilityCDMA::OnGetSignalQualityReply, 334 weak_ptr_factory_.GetWeakPtr()); 335 proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault); 336} 337 338void CellularCapabilityCDMA::GetRegistrationState() { 339 SLOG(this, 2) << __func__; 340 RegistrationStateCallback callback = 341 Bind(&CellularCapabilityCDMA::OnGetRegistrationStateReply, 342 weak_ptr_factory_.GetWeakPtr()); 343 proxy_->GetRegistrationState(nullptr, callback, kTimeoutDefault); 344} 345 346void CellularCapabilityCDMA::OnActivateReply( 347 const ResultCallback& callback, uint32_t status, const Error& error) { 348 activation_starting_ = false; 349 if (error.IsSuccess()) { 350 if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) { 351 activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING; 352 } else { 353 LOG(WARNING) << "Modem activation failed with status: " 354 << GetActivationErrorString(status) << " (" << status << ")"; 355 } 356 HandleNewActivationState(status); 357 } else { 358 LOG(ERROR) << "Activate() failed with error: " << error; 359 } 360 callback.Run(error); 361} 362 363void CellularCapabilityCDMA::OnGetRegistrationStateReply( 364 uint32_t state_1x, uint32_t state_evdo, const Error& error) { 365 SLOG(this, 2) << __func__; 366 if (error.IsSuccess()) 367 OnRegistrationStateChangedSignal(state_1x, state_evdo); 368} 369 370void CellularCapabilityCDMA::OnGetSignalQualityReply(uint32_t quality, 371 const Error& error) { 372 if (error.IsSuccess()) 373 OnSignalQualitySignal(quality); 374} 375 376void CellularCapabilityCDMA::OnActivationStateChangedSignal( 377 uint32_t activation_state, 378 uint32_t activation_error, 379 const KeyValueStore& status_changes) { 380 SLOG(this, 2) << __func__; 381 382 if (status_changes.ContainsString("mdn")) 383 cellular()->set_mdn(status_changes.GetString("mdn")); 384 if (status_changes.ContainsString("min")) 385 cellular()->set_min(status_changes.GetString("min")); 386 387 UpdateOnlinePortal(status_changes); 388 activation_state_ = activation_state; 389 HandleNewActivationState(activation_error); 390} 391 392void CellularCapabilityCDMA::OnRegistrationStateChangedSignal( 393 uint32_t state_1x, uint32_t state_evdo) { 394 SLOG(this, 2) << __func__; 395 registration_state_1x_ = state_1x; 396 registration_state_evdo_ = state_evdo; 397 cellular()->HandleNewRegistrationState(); 398} 399 400void CellularCapabilityCDMA::OnSignalQualitySignal(uint32_t strength) { 401 cellular()->HandleNewSignalQuality(strength); 402} 403 404void CellularCapabilityCDMA::UpdateOnlinePortal( 405 const KeyValueStore& properties) { 406 // Treat the three updates atomically: Only update the serving operator when 407 // all three are known: 408 if (properties.ContainsString("payment_url") && 409 properties.ContainsString("payment_url_method") && 410 properties.ContainsString("payment_url_postdata")) { 411 cellular()->home_provider_info()->UpdateOnlinePortal( 412 properties.GetString("payment_url"), 413 properties.GetString("payment_url_method"), 414 properties.GetString("payment_url_postdata")); 415 } 416} 417 418} // namespace shill 419