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
37 using base::Bind;
38 using std::string;
39 using std::vector;
40
41 namespace shill {
42
43 namespace Logging {
44 static auto kModuleLogScope = ScopeLogger::kCellular;
ObjectID(CellularCapabilityCDMA * c)45 static string ObjectID(CellularCapabilityCDMA* c) {
46 return c->cellular()->GetRpcIdentifier();
47 }
48 }
49
50 // static
51 const char CellularCapabilityCDMA::kPhoneNumber[] = "#777";
52
CellularCapabilityCDMA(Cellular * cellular,ControlInterface * control_interface,ModemInfo * modem_info)53 CellularCapabilityCDMA::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
~CellularCapabilityCDMA()66 CellularCapabilityCDMA::~CellularCapabilityCDMA() {}
67
InitProxies()68 void 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
GetTypeString() const83 string CellularCapabilityCDMA::GetTypeString() const {
84 return kTechnologyFamilyCdma;
85 }
86
StartModem(Error * error,const ResultCallback & callback)87 void 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
ReleaseProxies()111 void CellularCapabilityCDMA::ReleaseProxies() {
112 CellularCapabilityClassic::ReleaseProxies();
113 proxy_.reset();
114 }
115
AreProxiesInitialized() const116 bool CellularCapabilityCDMA::AreProxiesInitialized() const {
117 return (CellularCapabilityClassic::AreProxiesInitialized() && proxy_.get());
118 }
119
AllowRoaming()120 bool CellularCapabilityCDMA::AllowRoaming() {
121 return allow_roaming_property();
122 }
123
124
OnServiceCreated()125 void 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
UpdateStatus(const KeyValueStore & properties)133 void 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
UpdateServiceOLP()146 void 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
SetupConnectProperties(KeyValueStore * properties)167 void CellularCapabilityCDMA::SetupConnectProperties(
168 KeyValueStore* properties) {
169 properties->SetString(kConnectPropertyPhoneNumber, kPhoneNumber);
170 }
171
Activate(const string & carrier,Error * error,const ResultCallback & callback)172 void 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
HandleNewActivationState(uint32_t error)198 void 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
DisconnectCleanup()209 void 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
GetActivationStateString(uint32_t state)236 string 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
GetActivationErrorString(uint32_t error)252 string 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
GetMEID(const ResultCallback & callback)270 void 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
GetProperties(const ResultCallback & callback)280 void CellularCapabilityCDMA::GetProperties(const ResultCallback& callback) {
281 SLOG(this, 2) << __func__;
282 // No properties.
283 callback.Run(Error());
284 }
285
IsActivating() const286 bool CellularCapabilityCDMA::IsActivating() const {
287 return activation_starting_ ||
288 activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
289 }
290
IsRegistered() const291 bool 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
SetUnregistered(bool searching)296 void 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
GetNetworkTechnologyString() const301 string 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
GetRoamingStateString() const311 string 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
GetSignalQuality()330 void 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
GetRegistrationState()338 void 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
OnActivateReply(const ResultCallback & callback,uint32_t status,const Error & error)346 void 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
OnGetRegistrationStateReply(uint32_t state_1x,uint32_t state_evdo,const Error & error)363 void 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
OnGetSignalQualityReply(uint32_t quality,const Error & error)370 void CellularCapabilityCDMA::OnGetSignalQualityReply(uint32_t quality,
371 const Error& error) {
372 if (error.IsSuccess())
373 OnSignalQualitySignal(quality);
374 }
375
OnActivationStateChangedSignal(uint32_t activation_state,uint32_t activation_error,const KeyValueStore & status_changes)376 void 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
OnRegistrationStateChangedSignal(uint32_t state_1x,uint32_t state_evdo)392 void 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
OnSignalQualitySignal(uint32_t strength)400 void CellularCapabilityCDMA::OnSignalQualitySignal(uint32_t strength) {
401 cellular()->HandleNewSignalQuality(strength);
402 }
403
UpdateOnlinePortal(const KeyValueStore & properties)404 void 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