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