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_classic.h" 18 19#include <base/bind.h> 20#if defined(__ANDROID__) 21#include <dbus/service_constants.h> 22#else 23#include <chromeos/dbus/service_constants.h> 24#endif // __ANDROID__ 25 26#include "shill/cellular/cellular.h" 27#include "shill/cellular/modem_gobi_proxy_interface.h" 28#include "shill/control_interface.h" 29#include "shill/error.h" 30#include "shill/logging.h" 31#include "shill/property_accessor.h" 32 33using base::Bind; 34using base::Callback; 35using base::Closure; 36using std::string; 37 38namespace shill { 39 40namespace Logging { 41static auto kModuleLogScope = ScopeLogger::kCellular; 42static string ObjectID(CellularCapabilityClassic* c) { 43 return c->cellular()->GetRpcIdentifier(); 44} 45} 46 47const char CellularCapabilityClassic::kConnectPropertyApn[] = "apn"; 48const char CellularCapabilityClassic::kConnectPropertyApnUsername[] = 49 "username"; 50const char CellularCapabilityClassic::kConnectPropertyApnPassword[] = 51 "password"; 52const char CellularCapabilityClassic::kConnectPropertyHomeOnly[] = "home_only"; 53const char CellularCapabilityClassic::kConnectPropertyPhoneNumber[] = "number"; 54const char CellularCapabilityClassic::kModemPropertyEnabled[] = "Enabled"; 55const int CellularCapabilityClassic::kTimeoutSetCarrierMilliseconds = 120000; 56 57static Cellular::ModemState ConvertClassicToModemState(uint32_t classic_state) { 58 ModemClassicState cstate = static_cast<ModemClassicState>(classic_state); 59 switch (cstate) { 60 case kModemClassicStateUnknown: 61 return Cellular::kModemStateUnknown; 62 case kModemClassicStateDisabled: 63 return Cellular::kModemStateDisabled; 64 case kModemClassicStateDisabling: 65 return Cellular::kModemStateDisabling; 66 case kModemClassicStateEnabling: 67 return Cellular::kModemStateEnabling; 68 case kModemClassicStateEnabled: 69 return Cellular::kModemStateEnabled; 70 case kModemClassicStateSearching: 71 return Cellular::kModemStateSearching; 72 case kModemClassicStateRegistered: 73 return Cellular::kModemStateRegistered; 74 case kModemClassicStateDisconnecting: 75 return Cellular::kModemStateDisconnecting; 76 case kModemClassicStateConnecting: 77 return Cellular::kModemStateConnecting; 78 case kModemClassicStateConnected: 79 return Cellular::kModemStateConnected; 80 default: 81 return Cellular::kModemStateUnknown; 82 } 83} 84 85CellularCapabilityClassic::CellularCapabilityClassic( 86 Cellular* cellular, 87 ControlInterface* control_interface, 88 ModemInfo* modem_info) 89 : CellularCapability(cellular, control_interface, modem_info), 90 weak_ptr_factory_(this) { 91 // This class is currently instantiated only for Gobi modems so setup the 92 // supported carriers list appropriately and expose it over RPC. 93 cellular->set_supported_carriers({kCarrierGenericUMTS, 94 kCarrierSprint, 95 kCarrierVerizon}); 96} 97 98CellularCapabilityClassic::~CellularCapabilityClassic() {} 99 100void CellularCapabilityClassic::InitProxies() { 101 SLOG(this, 2) << __func__; 102 proxy_.reset(control_interface()->CreateModemProxy( 103 cellular()->dbus_path(), cellular()->dbus_service())); 104 simple_proxy_.reset(control_interface()->CreateModemSimpleProxy( 105 cellular()->dbus_path(), cellular()->dbus_service())); 106 proxy_->set_state_changed_callback( 107 Bind(&CellularCapabilityClassic::OnModemStateChangedSignal, 108 weak_ptr_factory_.GetWeakPtr())); 109} 110 111void CellularCapabilityClassic::ReleaseProxies() { 112 SLOG(this, 2) << __func__; 113 proxy_.reset(); 114 simple_proxy_.reset(); 115 gobi_proxy_.reset(); 116} 117 118bool CellularCapabilityClassic::AreProxiesInitialized() const { 119 return (proxy_.get() && simple_proxy_.get() && gobi_proxy_.get()); 120} 121 122void CellularCapabilityClassic::FinishEnable(const ResultCallback& callback) { 123 // Normally, running the callback is the last thing done in a method. 124 // In this case, we do it first, because we want to make sure that 125 // the device is marked as Enabled before the registration state is 126 // handled. See comment in Cellular::HandleNewRegistrationState. 127 callback.Run(Error()); 128 GetRegistrationState(); 129 GetSignalQuality(); 130 // We expect the modem to start scanning after it has been enabled. 131 // Change this if this behavior is no longer the case in the future. 132 modem_info()->metrics()->NotifyDeviceEnableFinished( 133 cellular()->interface_index()); 134 modem_info()->metrics()->NotifyDeviceScanStarted( 135 cellular()->interface_index()); 136} 137 138void CellularCapabilityClassic::FinishDisable(const ResultCallback& callback) { 139 modem_info()->metrics()->NotifyDeviceDisableFinished( 140 cellular()->interface_index()); 141 ReleaseProxies(); 142 callback.Run(Error()); 143} 144 145void CellularCapabilityClassic::RunNextStep(CellularTaskList* tasks) { 146 CHECK(!tasks->empty()); 147 SLOG(this, 2) << __func__ << ": " << tasks->size() << " remaining tasks"; 148 Closure task = (*tasks)[0]; 149 tasks->erase(tasks->begin()); 150 cellular()->dispatcher()->PostTask(task); 151} 152 153void CellularCapabilityClassic::StepCompletedCallback( 154 const ResultCallback& callback, 155 bool ignore_error, 156 CellularTaskList* tasks, 157 const Error& error) { 158 if ((ignore_error || error.IsSuccess()) && !tasks->empty()) { 159 RunNextStep(tasks); 160 return; 161 } 162 delete tasks; 163 if (!callback.is_null()) 164 callback.Run(error); 165} 166 167// always called from an async context 168void CellularCapabilityClassic::EnableModem(const ResultCallback& callback) { 169 SLOG(this, 2) << __func__; 170 CHECK(!callback.is_null()); 171 Error error; 172 modem_info()->metrics()->NotifyDeviceEnableStarted( 173 cellular()->interface_index()); 174 proxy_->Enable(true, &error, callback, kTimeoutEnable); 175 if (error.IsFailure()) 176 callback.Run(error); 177} 178 179// always called from an async context 180void CellularCapabilityClassic::DisableModem(const ResultCallback& callback) { 181 SLOG(this, 2) << __func__; 182 CHECK(!callback.is_null()); 183 Error error; 184 modem_info()->metrics()->NotifyDeviceDisableStarted( 185 cellular()->interface_index()); 186 proxy_->Enable(false, &error, callback, kTimeoutEnable); 187 if (error.IsFailure()) 188 callback.Run(error); 189} 190 191// always called from an async context 192void CellularCapabilityClassic::GetModemStatus(const ResultCallback& callback) { 193 SLOG(this, 2) << __func__; 194 CHECK(!callback.is_null()); 195 KeyValueStoreCallback cb = Bind( 196 &CellularCapabilityClassic::OnGetModemStatusReply, 197 weak_ptr_factory_.GetWeakPtr(), callback); 198 Error error; 199 simple_proxy_->GetModemStatus(&error, cb, kTimeoutDefault); 200 if (error.IsFailure()) 201 callback.Run(error); 202} 203 204// always called from an async context 205void CellularCapabilityClassic::GetModemInfo(const ResultCallback& callback) { 206 SLOG(this, 2) << __func__; 207 CHECK(!callback.is_null()); 208 ModemInfoCallback cb = Bind(&CellularCapabilityClassic::OnGetModemInfoReply, 209 weak_ptr_factory_.GetWeakPtr(), callback); 210 Error error; 211 proxy_->GetModemInfo(&error, cb, kTimeoutDefault); 212 if (error.IsFailure()) 213 callback.Run(error); 214} 215 216void CellularCapabilityClassic::StopModem(Error* error, 217 const ResultCallback& callback) { 218 SLOG(this, 2) << __func__; 219 220 CellularTaskList* tasks = new CellularTaskList(); 221 ResultCallback cb = 222 Bind(&CellularCapabilityClassic::StepCompletedCallback, 223 weak_ptr_factory_.GetWeakPtr(), callback, false, tasks); 224 ResultCallback cb_ignore_error = 225 Bind(&CellularCapabilityClassic::StepCompletedCallback, 226 weak_ptr_factory_.GetWeakPtr(), callback, true, tasks); 227 // TODO(ers): We can skip the call to Disconnect if the modem has 228 // told us that the modem state is Disabled or Registered. 229 tasks->push_back(Bind(&CellularCapabilityClassic::Disconnect, 230 weak_ptr_factory_.GetWeakPtr(), nullptr, 231 cb_ignore_error)); 232 // TODO(ers): We can skip the call to Disable if the modem has 233 // told us that the modem state is Disabled. 234 tasks->push_back(Bind(&CellularCapabilityClassic::DisableModem, 235 weak_ptr_factory_.GetWeakPtr(), cb)); 236 tasks->push_back(Bind(&CellularCapabilityClassic::FinishDisable, 237 weak_ptr_factory_.GetWeakPtr(), cb)); 238 239 RunNextStep(tasks); 240} 241 242void CellularCapabilityClassic::Connect(const KeyValueStore& properties, 243 Error* error, 244 const ResultCallback& callback) { 245 SLOG(this, 2) << __func__; 246 simple_proxy_->Connect(properties, error, callback, kTimeoutConnect); 247} 248 249void CellularCapabilityClassic::Disconnect(Error* error, 250 const ResultCallback& callback) { 251 SLOG(this, 2) << __func__; 252 if (proxy_.get()) 253 proxy_->Disconnect(error, callback, kTimeoutDisconnect); 254 else 255 LOG(ERROR) << "No proxy found in disconnect."; 256} 257 258void CellularCapabilityClassic::SetCarrier(const string& carrier, 259 Error* error, 260 const ResultCallback& callback) { 261 LOG(INFO) << __func__ << "(" << carrier << ")"; 262 if (!gobi_proxy_.get()) { 263 gobi_proxy_.reset(control_interface()->CreateModemGobiProxy( 264 cellular()->dbus_path(), cellular()->dbus_service())); 265 } 266 CHECK(error); 267 gobi_proxy_->SetCarrier(carrier, error, callback, 268 kTimeoutSetCarrierMilliseconds); 269} 270 271void CellularCapabilityClassic::OnPropertiesChanged( 272 const std::string& interface, 273 const KeyValueStore& changed_properties, 274 const std::vector<std::string>& invalidated_properties) { 275 SLOG(this, 2) << __func__; 276 // This solves a bootstrapping problem: If the modem is not yet 277 // enabled, there are no proxy objects associated with the capability 278 // object, so modem signals like StateChanged aren't seen. By monitoring 279 // changes to the Enabled property via the ModemManager, we're able to 280 // get the initialization process started, which will result in the 281 // creation of the proxy objects. 282 // 283 // We handle all state changes to ENABLED from a disabled state (including, 284 // UNKNOWN) through Cellular::OnModemStateChanged. This will try to enable 285 // the device regardless of whether it has been registered with the Manager. 286 // 287 // All other state changes are handled from OnModemStateChangedSignal. 288 if (changed_properties.ContainsBool(kModemPropertyEnabled)) { 289 bool enabled = changed_properties.GetBool(kModemPropertyEnabled); 290 SLOG(this, 2) << "Property \"Enabled\" changed: " << enabled; 291 Cellular::ModemState prev_modem_state = cellular()->modem_state(); 292 if (!Cellular::IsEnabledModemState(prev_modem_state)) { 293 cellular()->OnModemStateChanged( 294 enabled ? Cellular::kModemStateEnabled : 295 Cellular::kModemStateDisabled); 296 } 297 } 298} 299 300void CellularCapabilityClassic::OnGetModemStatusReply( 301 const ResultCallback& callback, 302 const KeyValueStore& props, 303 const Error& error) { 304 SLOG(this, 2) << __func__ << " error " << error; 305 if (error.IsSuccess()) { 306 if (props.ContainsString("carrier")) { 307 string carrier = props.GetString("carrier"); 308 cellular()->set_carrier(carrier); 309 cellular()->home_provider_info()->UpdateOperatorName(carrier); 310 } 311 if (props.ContainsString("meid")) { 312 cellular()->set_meid(props.GetString("meid")); 313 } 314 if (props.ContainsString("imei")) { 315 cellular()->set_imei(props.GetString("imei")); 316 } 317 if (props.ContainsString(kModemPropertyIMSI)) { 318 string imsi = props.GetString(kModemPropertyIMSI); 319 cellular()->set_imsi(imsi); 320 cellular()->home_provider_info()->UpdateIMSI(imsi); 321 // We do not currently obtain the IMSI OTA at all. Provide the IMSI from 322 // the SIM to the serving operator as well to aid in MVNO identification. 323 cellular()->serving_operator_info()->UpdateIMSI(imsi); 324 } 325 if (props.ContainsString("esn")) { 326 cellular()->set_esn(props.GetString("esn")); 327 } 328 if (props.ContainsString("mdn")) { 329 cellular()->set_mdn(props.GetString("mdn")); 330 } 331 if (props.ContainsString("min")) { 332 cellular()->set_min(props.GetString("min")); 333 } 334 if (props.ContainsString("firmware_revision")) { 335 cellular()->set_firmware_revision(props.GetString("firmware_revision")); 336 } 337 UpdateStatus(props); 338 } 339 callback.Run(error); 340} 341 342void CellularCapabilityClassic::UpdateStatus( 343 const KeyValueStore& properties) { 344 SLOG(this, 3) << __func__; 345} 346 347void CellularCapabilityClassic::OnGetModemInfoReply( 348 const ResultCallback& callback, 349 const std::string& manufacturer, 350 const std::string& modem, 351 const std::string& version, 352 const Error& error) { 353 SLOG(this, 2) << __func__ << "(" << error << ")"; 354 if (error.IsSuccess()) { 355 cellular()->set_manufacturer(manufacturer); 356 cellular()->set_model_id(modem); 357 cellular()->set_hardware_revision(version); 358 SLOG(this, 2) << __func__ << ": " << manufacturer << ", " << modem << ", " 359 << version; 360 } 361 callback.Run(error); 362} 363 364void CellularCapabilityClassic::OnModemStateChangedSignal( 365 uint32_t old_state, uint32_t new_state, uint32_t reason) { 366 SLOG(this, 2) << __func__ << "(" << old_state << ", " << new_state << ", " 367 << reason << ")"; 368 cellular()->OnModemStateChanged(ConvertClassicToModemState(new_state)); 369} 370 371} // namespace shill 372