1//
2// Copyright (C) 2013 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_universal.h"
18
19#include <base/bind.h>
20#include <base/stl_util.h>
21#include <base/strings/string_util.h>
22#include <base/strings/stringprintf.h>
23#if defined(__ANDROID__)
24#include <dbus/service_constants.h>
25#else
26#include <chromeos/dbus/service_constants.h>
27#endif  // __ANDROID__
28#include <ModemManager/ModemManager.h>
29
30#include <string>
31#include <vector>
32
33#include "shill/adaptor_interfaces.h"
34#include "shill/cellular/cellular_bearer.h"
35#include "shill/cellular/cellular_service.h"
36#include "shill/cellular/mobile_operator_info.h"
37#include "shill/control_interface.h"
38#include "shill/dbus_properties_proxy_interface.h"
39#include "shill/error.h"
40#include "shill/logging.h"
41#include "shill/pending_activation_store.h"
42#include "shill/property_accessor.h"
43
44#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
45#error "Do not include mm-modem.h"
46#endif
47
48using base::Bind;
49using base::Closure;
50using std::string;
51using std::vector;
52
53namespace shill {
54
55namespace Logging {
56static auto kModuleLogScope = ScopeLogger::kCellular;
57static string ObjectID(CellularCapabilityUniversal* c) {
58  return c->cellular()->GetRpcIdentifier();
59}
60}
61
62// static
63const char CellularCapabilityUniversal::kConnectPin[] = "pin";
64const char CellularCapabilityUniversal::kConnectOperatorId[] = "operator-id";
65const char CellularCapabilityUniversal::kConnectApn[] = "apn";
66const char CellularCapabilityUniversal::kConnectIPType[] = "ip-type";
67const char CellularCapabilityUniversal::kConnectUser[] = "user";
68const char CellularCapabilityUniversal::kConnectPassword[] = "password";
69const char CellularCapabilityUniversal::kConnectNumber[] = "number";
70const char CellularCapabilityUniversal::kConnectAllowRoaming[] =
71    "allow-roaming";
72const char CellularCapabilityUniversal::kConnectRMProtocol[] = "rm-protocol";
73const int64_t CellularCapabilityUniversal::kEnterPinTimeoutMilliseconds = 20000;
74const int64_t
75CellularCapabilityUniversal::kRegistrationDroppedUpdateTimeoutMilliseconds =
76    15000;
77const char CellularCapabilityUniversal::kRootPath[] = "/";
78const char CellularCapabilityUniversal::kStatusProperty[] = "status";
79const char CellularCapabilityUniversal::kOperatorLongProperty[] =
80    "operator-long";
81const char CellularCapabilityUniversal::kOperatorShortProperty[] =
82    "operator-short";
83const char CellularCapabilityUniversal::kOperatorCodeProperty[] =
84    "operator-code";
85const char CellularCapabilityUniversal::kOperatorAccessTechnologyProperty[] =
86    "access-technology";
87const char CellularCapabilityUniversal::kAltairLTEMMPlugin[] = "Altair LTE";
88const char CellularCapabilityUniversal::kNovatelLTEMMPlugin[] = "Novatel LTE";
89const int CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds =
90    20000;
91
92namespace {
93
94const char kPhoneNumber[] = "*99#";
95
96// This identifier is specified in the serviceproviders.prototxt file.
97const char kVzwIdentifier[] = "c83d6597-dc91-4d48-a3a7-d86b80123751";
98const size_t kVzwMdnLength = 10;
99
100string AccessTechnologyToString(uint32_t access_technologies) {
101  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
102    return kNetworkTechnologyLte;
103  if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
104                              MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
105                              MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
106    return kNetworkTechnologyEvdo;
107  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
108    return kNetworkTechnology1Xrtt;
109  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
110    return kNetworkTechnologyHspaPlus;
111  if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
112                              MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
113                              MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
114    return kNetworkTechnologyHspa;
115  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
116    return kNetworkTechnologyUmts;
117  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
118    return kNetworkTechnologyEdge;
119  if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
120    return kNetworkTechnologyGprs;
121  if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
122                              MM_MODEM_ACCESS_TECHNOLOGY_GSM))
123      return kNetworkTechnologyGsm;
124  return "";
125}
126
127string AccessTechnologyToTechnologyFamily(uint32_t access_technologies) {
128  if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_LTE |
129                             MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS |
130                             MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
131                             MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
132                             MM_MODEM_ACCESS_TECHNOLOGY_HSDPA |
133                             MM_MODEM_ACCESS_TECHNOLOGY_UMTS |
134                             MM_MODEM_ACCESS_TECHNOLOGY_EDGE |
135                             MM_MODEM_ACCESS_TECHNOLOGY_GPRS |
136                             MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
137                             MM_MODEM_ACCESS_TECHNOLOGY_GSM))
138    return kTechnologyFamilyGsm;
139  if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
140                             MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
141                             MM_MODEM_ACCESS_TECHNOLOGY_EVDOB |
142                             MM_MODEM_ACCESS_TECHNOLOGY_1XRTT))
143    return kTechnologyFamilyCdma;
144  return "";
145}
146
147}  // namespace
148
149CellularCapabilityUniversal::CellularCapabilityUniversal(
150    Cellular* cellular,
151    ControlInterface* control_interface,
152    ModemInfo* modem_info)
153    : CellularCapability(cellular, control_interface, modem_info),
154      mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
155                                                   "ParseScanResult")),
156      weak_ptr_factory_(this),
157      registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
158      current_capabilities_(MM_MODEM_CAPABILITY_NONE),
159      access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
160      resetting_(false),
161      subscription_state_(kSubscriptionStateUnknown),
162      reset_done_(false),
163      registration_dropped_update_timeout_milliseconds_(
164          kRegistrationDroppedUpdateTimeoutMilliseconds) {
165  SLOG(this, 2) << "Cellular capability constructed: Universal";
166  mobile_operator_info_->Init();
167  HelpRegisterConstDerivedKeyValueStore(
168      kSIMLockStatusProperty,
169      &CellularCapabilityUniversal::SimLockStatusToProperty);
170}
171
172CellularCapabilityUniversal::~CellularCapabilityUniversal() {}
173
174KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
175    Error* /*error*/) {
176  KeyValueStore status;
177  string lock_type;
178  switch (sim_lock_status_.lock_type) {
179    case MM_MODEM_LOCK_SIM_PIN:
180      lock_type = "sim-pin";
181      break;
182    case MM_MODEM_LOCK_SIM_PUK:
183      lock_type = "sim-puk";
184      break;
185    default:
186      lock_type = "";
187      break;
188  }
189  status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
190  status.SetString(kSIMLockTypeProperty, lock_type);
191  status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
192  return status;
193}
194
195void CellularCapabilityUniversal::HelpRegisterConstDerivedKeyValueStore(
196    const string& name,
197    KeyValueStore(CellularCapabilityUniversal::*get)(Error* error)) {
198  cellular()->mutable_store()->RegisterDerivedKeyValueStore(
199      name,
200      KeyValueStoreAccessor(
201          new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
202              this, get, nullptr)));
203}
204
205void CellularCapabilityUniversal::InitProxies() {
206  modem_3gpp_proxy_.reset(
207      control_interface()->CreateMM1ModemModem3gppProxy(
208          cellular()->dbus_path(), cellular()->dbus_service()));
209  modem_proxy_.reset(
210      control_interface()->CreateMM1ModemProxy(cellular()->dbus_path(),
211                                               cellular()->dbus_service()));
212  modem_simple_proxy_.reset(
213      control_interface()->CreateMM1ModemSimpleProxy(
214          cellular()->dbus_path(), cellular()->dbus_service()));
215
216  modem_proxy_->set_state_changed_callback(
217      Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
218           weak_ptr_factory_.GetWeakPtr()));
219  // Do not create a SIM proxy until the device is enabled because we
220  // do not yet know the object path of the sim object.
221  // TODO(jglasgow): register callbacks
222}
223
224void CellularCapabilityUniversal::StartModem(Error* error,
225                                             const ResultCallback& callback) {
226  SLOG(this, 3) << __func__;
227  InitProxies();
228  deferred_enable_modem_callback_.Reset();
229  EnableModem(true, error, callback);
230}
231
232void CellularCapabilityUniversal::EnableModem(bool deferrable,
233                                              Error* error,
234                                              const ResultCallback& callback) {
235  SLOG(this, 3) << __func__ << "(deferrable=" << deferrable << ")";
236  CHECK(!callback.is_null());
237  Error local_error(Error::kOperationInitiated);
238  modem_info()->metrics()->NotifyDeviceEnableStarted(
239      cellular()->interface_index());
240  modem_proxy_->Enable(
241      true,
242      &local_error,
243      Bind(&CellularCapabilityUniversal::EnableModemCompleted,
244           weak_ptr_factory_.GetWeakPtr(), deferrable, callback),
245      kTimeoutEnable);
246  if (local_error.IsFailure()) {
247    SLOG(this, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
248  }
249  if (error) {
250    error->CopyFrom(local_error);
251  }
252}
253
254void CellularCapabilityUniversal::EnableModemCompleted(
255    bool deferrable, const ResultCallback& callback, const Error& error) {
256  SLOG(this, 3) << __func__ << "(deferrable=" << deferrable
257                            << ", error=" << error << ")";
258
259  // If the enable operation failed with Error::kWrongState, the modem is not
260  // in the expected state (i.e. disabled). If |deferrable| indicates that the
261  // enable operation can be deferred, we defer the operation until the modem
262  // goes into the expected state (see OnModemStateChangedSignal).
263  //
264  // Note that when the SIM is locked, the enable operation also fails with
265  // Error::kWrongState. The enable operation is deferred until the modem goes
266  // into the disabled state after the SIM is unlocked. We may choose not to
267  // defer the enable operation when the SIM is locked, but the UI needs to
268  // trigger the enable operation after the SIM is unlocked, which is currently
269  // not the case.
270  if (error.IsFailure()) {
271    if (!deferrable || error.type() != Error::kWrongState) {
272      callback.Run(error);
273      return;
274    }
275
276    if (deferred_enable_modem_callback_.is_null()) {
277      SLOG(this, 2) << "Defer enable operation.";
278      // The Enable operation to be deferred should not be further deferrable.
279      deferred_enable_modem_callback_ =
280          Bind(&CellularCapabilityUniversal::EnableModem,
281               weak_ptr_factory_.GetWeakPtr(),
282               false,  // non-deferrable
283               nullptr,
284               callback);
285    }
286    return;
287  }
288
289  // After modem is enabled, it should be possible to get properties
290  // TODO(jglasgow): handle errors from GetProperties
291  GetProperties();
292  // We expect the modem to start scanning after it has been enabled.
293  // Change this if this behavior is no longer the case in the future.
294  modem_info()->metrics()->NotifyDeviceEnableFinished(
295      cellular()->interface_index());
296  modem_info()->metrics()->NotifyDeviceScanStarted(
297      cellular()->interface_index());
298  callback.Run(error);
299}
300
301void CellularCapabilityUniversal::StopModem(Error* error,
302                                            const ResultCallback& callback) {
303  CHECK(!callback.is_null());
304  CHECK(error);
305  // If there is an outstanding registration change, simply ignore it since
306  // the service will be destroyed anyway.
307  if (!registration_dropped_update_callback_.IsCancelled()) {
308    registration_dropped_update_callback_.Cancel();
309    SLOG(this, 2) << __func__ << " Cancelled delayed deregister.";
310  }
311
312  // Some modems will implicitly disconnect the bearer when transitioning to
313  // low power state. For such modems, it's faster to let the modem disconnect
314  // the bearer. To do that, we just remove the bearer from the list so
315  // ModemManager doesn't try to disconnect it during disable.
316  Closure task;
317  if (cellular()->mm_plugin() == kAltairLTEMMPlugin) {
318    task = Bind(&CellularCapabilityUniversal::Stop_DeleteActiveBearer,
319                weak_ptr_factory_.GetWeakPtr(),
320                callback);
321  } else {
322    task = Bind(&CellularCapabilityUniversal::Stop_Disable,
323                weak_ptr_factory_.GetWeakPtr(),
324                callback);
325  }
326  cellular()->dispatcher()->PostTask(task);
327  deferred_enable_modem_callback_.Reset();
328}
329
330void CellularCapabilityUniversal::Stop_DeleteActiveBearer(
331    const ResultCallback& callback) {
332  SLOG(this, 3) << __func__;
333
334  if (!active_bearer_) {
335    Stop_Disable(callback);
336    return;
337  }
338
339  Error error;
340  modem_proxy_->DeleteBearer(
341      active_bearer_->dbus_path(), &error,
342      Bind(&CellularCapabilityUniversal::Stop_DeleteActiveBearerCompleted,
343           weak_ptr_factory_.GetWeakPtr(), callback),
344      kTimeoutDefault);
345  if (error.IsFailure())
346    callback.Run(error);
347}
348
349void CellularCapabilityUniversal::Stop_DeleteActiveBearerCompleted(
350    const ResultCallback& callback, const Error& error) {
351  SLOG(this, 3) << __func__;
352  // Disregard the error from the bearer deletion since the disable will clean
353  // up any remaining bearers.
354  Stop_Disable(callback);
355}
356
357void CellularCapabilityUniversal::Stop_Disable(const ResultCallback& callback) {
358  SLOG(this, 3) << __func__;
359  Error error;
360  modem_info()->metrics()->NotifyDeviceDisableStarted(
361      cellular()->interface_index());
362  modem_proxy_->Enable(
363      false, &error,
364      Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
365           weak_ptr_factory_.GetWeakPtr(), callback),
366      kTimeoutEnable);
367  if (error.IsFailure())
368    callback.Run(error);
369}
370
371void CellularCapabilityUniversal::Stop_DisableCompleted(
372    const ResultCallback& callback, const Error& error) {
373  SLOG(this, 3) << __func__;
374
375  if (error.IsSuccess()) {
376    // The modem has been successfully disabled, but we still need to power it
377    // down.
378    Stop_PowerDown(callback);
379  } else {
380    // An error occurred; terminate the disable sequence.
381    callback.Run(error);
382  }
383}
384
385void CellularCapabilityUniversal::Stop_PowerDown(
386    const ResultCallback& callback) {
387  SLOG(this, 3) << __func__;
388  Error error;
389  modem_proxy_->SetPowerState(
390      MM_MODEM_POWER_STATE_LOW,
391      &error,
392      Bind(&CellularCapabilityUniversal::Stop_PowerDownCompleted,
393           weak_ptr_factory_.GetWeakPtr(), callback),
394      kSetPowerStateTimeoutMilliseconds);
395
396  if (error.IsFailure())
397    // This really shouldn't happen, but if it does, report success,
398    // because a stop initiated power down is only called if the
399    // modem was successfully disabled, but the failure of this
400    // operation should still be propagated up as a successful disable.
401    Stop_PowerDownCompleted(callback, error);
402}
403
404// Note: if we were in the middle of powering down the modem when the
405// system suspended, we might not get this event from
406// ModemManager. And we might not even get a timeout from dbus-c++,
407// because StartModem re-initializes proxies.
408void CellularCapabilityUniversal::Stop_PowerDownCompleted(
409    const ResultCallback& callback,
410    const Error& error) {
411  SLOG(this, 3) << __func__;
412
413  if (error.IsFailure())
414    SLOG(this, 2) << "Ignoring error returned by SetPowerState: " << error;
415
416  // Since the disable succeeded, if power down fails, we currently fail
417  // silently, i.e. we need to report the disable operation as having
418  // succeeded.
419  modem_info()->metrics()->NotifyDeviceDisableFinished(
420      cellular()->interface_index());
421  ReleaseProxies();
422  callback.Run(Error());
423}
424
425void CellularCapabilityUniversal::Connect(const KeyValueStore& properties,
426                                          Error* error,
427                                          const ResultCallback& callback) {
428  SLOG(this, 3) << __func__;
429  RpcIdentifierCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
430                                  weak_ptr_factory_.GetWeakPtr(),
431                                  callback);
432  modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
433}
434
435void CellularCapabilityUniversal::Disconnect(Error* error,
436                                             const ResultCallback& callback) {
437  SLOG(this, 3) << __func__;
438  if (modem_simple_proxy_.get()) {
439    SLOG(this, 2) << "Disconnect all bearers.";
440    // If "/" is passed as the bearer path, ModemManager will disconnect all
441    // bearers.
442    modem_simple_proxy_->Disconnect(kRootPath,
443                                    error,
444                                    callback,
445                                    kTimeoutDisconnect);
446  }
447}
448
449void CellularCapabilityUniversal::CompleteActivation(Error* error) {
450  SLOG(this, 3) << __func__;
451
452  // Persist the ICCID as "Pending Activation".
453  // We're assuming that when this function gets called,
454  // |cellular()->sim_identifier()| will be non-empty. We still check here that
455  // is non-empty, though something is wrong if it is empty.
456  const string& sim_identifier = cellular()->sim_identifier();
457  if (sim_identifier.empty()) {
458    SLOG(this, 2) << "SIM identifier not available. Nothing to do.";
459    return;
460  }
461
462  modem_info()->pending_activation_store()->SetActivationState(
463      PendingActivationStore::kIdentifierICCID,
464      sim_identifier,
465      PendingActivationStore::kStatePending);
466  UpdatePendingActivationState();
467
468  SLOG(this, 2) << "Resetting modem for activation.";
469  ResetAfterActivation();
470}
471
472void CellularCapabilityUniversal::ResetAfterActivation() {
473  SLOG(this, 3) << __func__;
474
475  // Here the initial call to Reset might fail in rare cases. Simply ignore.
476  Error error;
477  ResultCallback callback = Bind(
478      &CellularCapabilityUniversal::OnResetAfterActivationReply,
479      weak_ptr_factory_.GetWeakPtr());
480  Reset(&error, callback);
481  if (error.IsFailure())
482    SLOG(this, 2) << "Failed to reset after activation.";
483}
484
485void CellularCapabilityUniversal::OnResetAfterActivationReply(
486    const Error& error) {
487  SLOG(this, 3) << __func__;
488  if (error.IsFailure()) {
489    SLOG(this, 2) << "Failed to reset after activation. Try again later.";
490    // TODO(armansito): Maybe post a delayed reset task?
491    return;
492  }
493  reset_done_ = true;
494  UpdatePendingActivationState();
495}
496
497void CellularCapabilityUniversal::UpdatePendingActivationState() {
498  SLOG(this, 3) << __func__;
499
500  const string& sim_identifier = cellular()->sim_identifier();
501  bool registered =
502      registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
503
504  // We know a service is activated if |subscription_state_| is
505  // kSubscriptionStateProvisioned / kSubscriptionStateOutOfData
506  // In the case that |subscription_state_| is kSubscriptionStateUnknown, we
507  // fallback on checking for a valid MDN.
508  bool activated =
509    ((subscription_state_ == kSubscriptionStateProvisioned) ||
510     (subscription_state_ == kSubscriptionStateOutOfData)) ||
511    ((subscription_state_ == kSubscriptionStateUnknown) && IsMdnValid());
512
513  if (activated && !sim_identifier.empty())
514      modem_info()->pending_activation_store()->RemoveEntry(
515          PendingActivationStore::kIdentifierICCID,
516          sim_identifier);
517
518  CellularServiceRefPtr service = cellular()->service();
519
520  if (!service.get())
521    return;
522
523  if (service->activation_state() == kActivationStateActivated)
524      // Either no service or already activated. Nothing to do.
525      return;
526
527  // If the ICCID is not available, the following logic can be delayed until it
528  // becomes available.
529  if (sim_identifier.empty())
530    return;
531
532  PendingActivationStore::State state =
533      modem_info()->pending_activation_store()->GetActivationState(
534          PendingActivationStore::kIdentifierICCID,
535          sim_identifier);
536  switch (state) {
537    case PendingActivationStore::kStatePending:
538      // Always mark the service as activating here, as the ICCID could have
539      // been unavailable earlier.
540      service->SetActivationState(kActivationStateActivating);
541      if (reset_done_) {
542        SLOG(this, 2) << "Post-payment activation reset complete.";
543        modem_info()->pending_activation_store()->SetActivationState(
544            PendingActivationStore::kIdentifierICCID,
545            sim_identifier,
546            PendingActivationStore::kStateActivated);
547      }
548      break;
549    case PendingActivationStore::kStateActivated:
550      if (registered) {
551        // Trigger auto connect here.
552        SLOG(this, 2) << "Modem has been reset at least once, try to "
553                      << "autoconnect to force MDN to update.";
554        service->AutoConnect();
555      }
556      break;
557    case PendingActivationStore::kStateUnknown:
558      // No entry exists for this ICCID. Nothing to do.
559      break;
560    default:
561      NOTREACHED();
562  }
563}
564
565string CellularCapabilityUniversal::GetMdnForOLP(
566    const MobileOperatorInfo* operator_info) const {
567  // TODO(benchan): This is ugly. Remove carrier specific code once we move
568  // mobile activation logic to carrier-specifc extensions (crbug.com/260073).
569  const string& mdn = cellular()->mdn();
570  if (!operator_info->IsMobileNetworkOperatorKnown()) {
571    // Can't make any carrier specific modifications.
572    return mdn;
573  }
574
575  if (operator_info->uuid() == kVzwIdentifier) {
576    // subscription_state_ is the definitive indicator of whether we need
577    // activation. The OLP expects an all zero MDN in that case.
578    if (subscription_state_ == kSubscriptionStateUnprovisioned || mdn.empty()) {
579      return string(kVzwMdnLength, '0');
580    }
581    if (mdn.length() > kVzwMdnLength) {
582      return mdn.substr(mdn.length() - kVzwMdnLength);
583    }
584  }
585  return mdn;
586}
587
588void CellularCapabilityUniversal::ReleaseProxies() {
589  SLOG(this, 3) << __func__;
590  modem_3gpp_proxy_.reset();
591  modem_proxy_.reset();
592  modem_simple_proxy_.reset();
593  sim_proxy_.reset();
594}
595
596bool CellularCapabilityUniversal::AreProxiesInitialized() const {
597  return (modem_3gpp_proxy_.get() && modem_proxy_.get() &&
598          modem_simple_proxy_.get() && sim_proxy_.get());
599}
600
601void CellularCapabilityUniversal::UpdateServiceActivationState() {
602  if (!cellular()->service().get())
603    return;
604
605  const string& sim_identifier = cellular()->sim_identifier();
606  string activation_state;
607  PendingActivationStore::State state =
608      modem_info()->pending_activation_store()->GetActivationState(
609          PendingActivationStore::kIdentifierICCID,
610          sim_identifier);
611  if ((subscription_state_ == kSubscriptionStateUnknown ||
612       subscription_state_ == kSubscriptionStateUnprovisioned) &&
613      !sim_identifier.empty() &&
614      state == PendingActivationStore::kStatePending) {
615    activation_state = kActivationStateActivating;
616  } else if (IsServiceActivationRequired()) {
617    activation_state = kActivationStateNotActivated;
618  } else {
619    activation_state = kActivationStateActivated;
620
621    // Mark an activated service for auto-connect by default. Since data from
622    // the user profile will be loaded after the call to OnServiceCreated, this
623    // property will be corrected based on the user data at that time.
624    // NOTE: This function can be called outside the service initialization
625    // path so make sure we don't overwrite the auto-connect setting.
626    if (cellular()->service()->activation_state() != activation_state)
627      cellular()->service()->SetAutoConnect(true);
628  }
629  cellular()->service()->SetActivationState(activation_state);
630}
631
632void CellularCapabilityUniversal::OnServiceCreated() {
633  cellular()->service()->SetActivationType(CellularService::kActivationTypeOTA);
634  UpdateServiceActivationState();
635
636  // WORKAROUND:
637  // E362 modems on Verizon network does not properly redirect when a SIM
638  // runs out of credits, we need to enforce out-of-credits detection.
639  //
640  // The out-of-credits detection is also needed on ALT3100 modems until the PCO
641  // support is ready (crosbug.com/p/20461).
642  cellular()->service()->InitOutOfCreditsDetection(
643      GetOutOfCreditsDetectionType());
644
645  // Make sure that the network technology is set when the service gets
646  // created, just in case.
647  cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
648}
649
650// Create the list of APNs to try, in the following order:
651// - last APN that resulted in a successful connection attempt on the
652//   current network (if any)
653// - the APN, if any, that was set by the user
654// - the list of APNs found in the mobile broadband provider DB for the
655//   home provider associated with the current SIM
656// - as a last resort, attempt to connect with no APN
657void CellularCapabilityUniversal::SetupApnTryList() {
658  apn_try_list_.clear();
659
660  DCHECK(cellular()->service().get());
661  const Stringmap* apn_info = cellular()->service()->GetLastGoodApn();
662  if (apn_info)
663    apn_try_list_.push_back(*apn_info);
664
665  apn_info = cellular()->service()->GetUserSpecifiedApn();
666  if (apn_info)
667    apn_try_list_.push_back(*apn_info);
668
669  apn_try_list_.insert(apn_try_list_.end(),
670                       cellular()->apn_list().begin(),
671                       cellular()->apn_list().end());
672}
673
674void CellularCapabilityUniversal::SetupConnectProperties(
675    KeyValueStore* properties) {
676  SetupApnTryList();
677  FillConnectPropertyMap(properties);
678}
679
680void CellularCapabilityUniversal::FillConnectPropertyMap(
681    KeyValueStore* properties) {
682
683  // TODO(jglasgow): Is this really needed anymore?
684  properties->SetString(kConnectNumber, kPhoneNumber);
685
686  properties->SetBool(kConnectAllowRoaming, AllowRoaming());
687
688  if (!apn_try_list_.empty()) {
689    // Leave the APN at the front of the list, so that it can be recorded
690    // if the connect attempt succeeds.
691    Stringmap apn_info = apn_try_list_.front();
692    SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
693    properties->SetString(kConnectApn, apn_info[kApnProperty]);
694    if (ContainsKey(apn_info, kApnUsernameProperty))
695      properties->SetString(kConnectUser, apn_info[kApnUsernameProperty]);
696    if (ContainsKey(apn_info, kApnPasswordProperty))
697      properties->SetString(kConnectPassword, apn_info[kApnPasswordProperty]);
698  }
699}
700
701void CellularCapabilityUniversal::OnConnectReply(const ResultCallback& callback,
702                                                 const string& path,
703                                                 const Error& error) {
704  SLOG(this, 3) << __func__ << "(" << error << ")";
705
706  CellularServiceRefPtr service = cellular()->service();
707  if (!service) {
708    // The service could have been deleted before our Connect() request
709    // completes if the modem was enabled and then quickly disabled.
710    apn_try_list_.clear();
711  } else if (error.IsFailure()) {
712    service->ClearLastGoodApn();
713    // The APN that was just tried (and failed) is still at the
714    // front of the list, about to be removed. If the list is empty
715    // after that, try one last time without an APN. This may succeed
716    // with some modems in some cases.
717    if (RetriableConnectError(error) && !apn_try_list_.empty()) {
718      apn_try_list_.pop_front();
719      SLOG(this, 2) << "Connect failed with invalid APN, "
720                    << apn_try_list_.size() << " remaining APNs to try";
721      KeyValueStore props;
722      FillConnectPropertyMap(&props);
723      Error error;
724      Connect(props, &error, callback);
725      return;
726    }
727  } else {
728    if (!apn_try_list_.empty()) {
729      service->SetLastGoodApn(apn_try_list_.front());
730      apn_try_list_.clear();
731    }
732    SLOG(this, 2) << "Connected bearer " << path;
733  }
734
735  if (!callback.is_null())
736    callback.Run(error);
737
738  UpdatePendingActivationState();
739}
740
741bool CellularCapabilityUniversal::AllowRoaming() {
742  return cellular()->provider_requires_roaming() || allow_roaming_property();
743}
744
745void CellularCapabilityUniversal::GetProperties() {
746  SLOG(this, 3) << __func__;
747
748  std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
749      control_interface()->CreateDBusPropertiesProxy(
750          cellular()->dbus_path(), cellular()->dbus_service()));
751
752  KeyValueStore properties(
753      properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
754  OnModemPropertiesChanged(properties, vector<string>());
755
756  properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
757  OnModem3GPPPropertiesChanged(properties, vector<string>());
758}
759
760void CellularCapabilityUniversal::UpdateServiceOLP() {
761  SLOG(this, 3) << __func__;
762
763  // OLP is based off of the Home Provider.
764  if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown()) {
765    return;
766  }
767
768  const vector<MobileOperatorInfo::OnlinePortal>& olp_list =
769      cellular()->home_provider_info()->olp_list();
770  if (olp_list.empty()) {
771    return;
772  }
773
774  if (olp_list.size() > 1) {
775    SLOG(this, 1) << "Found multiple online portals. Choosing the first.";
776  }
777  string post_data = olp_list[0].post_data;
778  base::ReplaceSubstringsAfterOffset(
779      &post_data, 0, "${iccid}", cellular()->sim_identifier());
780  base::ReplaceSubstringsAfterOffset(
781      &post_data, 0, "${imei}", cellular()->imei());
782  base::ReplaceSubstringsAfterOffset(
783      &post_data, 0, "${imsi}", cellular()->imsi());
784  base::ReplaceSubstringsAfterOffset(
785      &post_data, 0, "${mdn}", GetMdnForOLP(cellular()->home_provider_info()));
786  base::ReplaceSubstringsAfterOffset(
787      &post_data, 0, "${min}", cellular()->min());
788  cellular()->service()->SetOLP(olp_list[0].url, olp_list[0].method, post_data);
789}
790
791void CellularCapabilityUniversal::UpdateActiveBearer() {
792  SLOG(this, 3) << __func__;
793
794  // Look for the first active bearer and use its path as the connected
795  // one. Right now, we don't allow more than one active bearer.
796  active_bearer_.reset();
797  for (const auto& path : bearer_paths_) {
798    std::unique_ptr<CellularBearer> bearer(
799        new CellularBearer(control_interface(),
800                           path,
801                           cellular()->dbus_service()));
802    // The bearer object may have vanished before ModemManager updates the
803    // 'Bearers' property.
804    if (!bearer->Init())
805      continue;
806
807    if (!bearer->connected())
808      continue;
809
810    SLOG(this, 2) << "Found active bearer \"" << path << "\".";
811    CHECK(!active_bearer_) << "Found more than one active bearer.";
812    active_bearer_ = std::move(bearer);
813  }
814
815  if (!active_bearer_)
816    SLOG(this, 2) << "No active bearer found.";
817}
818
819bool CellularCapabilityUniversal::IsServiceActivationRequired() const {
820  const string& sim_identifier = cellular()->sim_identifier();
821  // subscription_state_ is the definitive answer. If that does not work,
822  // fallback on MDN based logic.
823  if (subscription_state_ == kSubscriptionStateProvisioned ||
824      subscription_state_ == kSubscriptionStateOutOfData)
825    return false;
826
827  // We are in the process of activating, ignore all other clues from the
828  // network and use our own knowledge about the activation state.
829  if (!sim_identifier.empty() &&
830      modem_info()->pending_activation_store()->GetActivationState(
831          PendingActivationStore::kIdentifierICCID,
832          sim_identifier) != PendingActivationStore::kStateUnknown)
833    return false;
834
835  // Network notification that the service needs to be activated.
836  if (subscription_state_ == kSubscriptionStateUnprovisioned)
837    return true;
838
839  // If there is no online payment portal information, it's safer to assume
840  // the service does not require activation.
841  if (!cellular()->home_provider_info()->IsMobileNetworkOperatorKnown() ||
842      cellular()->home_provider_info()->olp_list().empty()) {
843    return false;
844  }
845
846  // If the MDN is invalid (i.e. empty or contains only zeros), the service
847  // requires activation.
848  return !IsMdnValid();
849}
850
851bool CellularCapabilityUniversal::IsMdnValid() const {
852  const string& mdn = cellular()->mdn();
853  // Note that |mdn| is normalized to contain only digits in OnMdnChanged().
854  for (size_t i = 0; i < mdn.size(); ++i) {
855    if (mdn[i] != '0')
856      return true;
857  }
858  return false;
859}
860
861// always called from an async context
862void CellularCapabilityUniversal::Register(const ResultCallback& callback) {
863  SLOG(this, 3) << __func__ << " \"" << cellular()->selected_network()
864                            << "\"";
865  CHECK(!callback.is_null());
866  Error error;
867  ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
868                                weak_ptr_factory_.GetWeakPtr(), callback);
869  modem_3gpp_proxy_->Register(cellular()->selected_network(), &error, cb,
870                              kTimeoutRegister);
871  if (error.IsFailure())
872    callback.Run(error);
873}
874
875void CellularCapabilityUniversal::RegisterOnNetwork(
876    const string& network_id,
877    Error* error,
878    const ResultCallback& callback) {
879  SLOG(this, 3) << __func__ << "(" << network_id << ")";
880  CHECK(error);
881  desired_network_ = network_id;
882  ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
883                                weak_ptr_factory_.GetWeakPtr(), callback);
884  modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
885}
886
887void CellularCapabilityUniversal::OnRegisterReply(
888    const ResultCallback& callback,
889    const Error& error) {
890  SLOG(this, 3) << __func__ << "(" << error << ")";
891
892  if (error.IsSuccess()) {
893    cellular()->set_selected_network(desired_network_);
894    desired_network_.clear();
895    callback.Run(error);
896    return;
897  }
898  // If registration on the desired network failed,
899  // try to register on the home network.
900  if (!desired_network_.empty()) {
901    desired_network_.clear();
902    cellular()->set_selected_network("");
903    LOG(INFO) << "Couldn't register on selected network, trying home network";
904    Register(callback);
905    return;
906  }
907  callback.Run(error);
908}
909
910bool CellularCapabilityUniversal::IsRegistered() const {
911  return IsRegisteredState(registration_state_);
912}
913
914bool CellularCapabilityUniversal::IsRegisteredState(
915    MMModem3gppRegistrationState state) {
916  return (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
917          state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
918}
919
920void CellularCapabilityUniversal::SetUnregistered(bool searching) {
921  // If we're already in some non-registered state, don't override that
922  if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
923          registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
924    registration_state_ =
925        (searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
926                     MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
927  }
928}
929
930void CellularCapabilityUniversal::RequirePIN(
931    const string& pin, bool require,
932    Error* error, const ResultCallback& callback) {
933  CHECK(error);
934  sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
935}
936
937void CellularCapabilityUniversal::EnterPIN(const string& pin,
938                                           Error* error,
939                                           const ResultCallback& callback) {
940  CHECK(error);
941  SLOG(this, 3) << __func__;
942  sim_proxy_->SendPin(pin, error, callback, kEnterPinTimeoutMilliseconds);
943}
944
945void CellularCapabilityUniversal::UnblockPIN(const string& unblock_code,
946                                             const string& pin,
947                                             Error* error,
948                                             const ResultCallback& callback) {
949  CHECK(error);
950  sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
951}
952
953void CellularCapabilityUniversal::ChangePIN(
954    const string& old_pin, const string& new_pin,
955    Error* error, const ResultCallback& callback) {
956  CHECK(error);
957  sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
958}
959
960void CellularCapabilityUniversal::Reset(Error* error,
961                                        const ResultCallback& callback) {
962  SLOG(this, 3) << __func__;
963  CHECK(error);
964  if (resetting_) {
965    Error::PopulateAndLog(FROM_HERE, error, Error::kInProgress,
966                          "Already resetting");
967    return;
968  }
969  ResultCallback cb = Bind(&CellularCapabilityUniversal::OnResetReply,
970                           weak_ptr_factory_.GetWeakPtr(), callback);
971  modem_proxy_->Reset(error, cb, kTimeoutReset);
972  if (!error->IsFailure()) {
973    resetting_ = true;
974  }
975}
976
977void CellularCapabilityUniversal::OnResetReply(const ResultCallback& callback,
978                                               const Error& error) {
979  SLOG(this, 3) << __func__;
980  resetting_ = false;
981  if (!callback.is_null())
982    callback.Run(error);
983}
984
985void CellularCapabilityUniversal::Scan(
986    Error* error,
987    const ResultStringmapsCallback& callback) {
988  KeyValueStoresCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
989                                   weak_ptr_factory_.GetWeakPtr(), callback);
990  modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
991}
992
993void CellularCapabilityUniversal::OnScanReply(
994    const ResultStringmapsCallback& callback,
995    const ScanResults& results,
996    const Error& error) {
997  Stringmaps found_networks;
998  for (const auto& result : results)
999    found_networks.push_back(ParseScanResult(result));
1000  callback.Run(found_networks, error);
1001}
1002
1003Stringmap CellularCapabilityUniversal::ParseScanResult(
1004    const ScanResult& result) {
1005
1006  /* ScanResults contain the following keys:
1007
1008     "status"
1009     A MMModem3gppNetworkAvailability value representing network
1010     availability status, given as an unsigned integer (signature "u").
1011     This key will always be present.
1012
1013     "operator-long"
1014     Long-format name of operator, given as a string value (signature
1015     "s"). If the name is unknown, this field should not be present.
1016
1017     "operator-short"
1018     Short-format name of operator, given as a string value
1019     (signature "s"). If the name is unknown, this field should not
1020     be present.
1021
1022     "operator-code"
1023     Mobile code of the operator, given as a string value (signature
1024     "s"). Returned in the format "MCCMNC", where MCC is the
1025     three-digit ITU E.212 Mobile Country Code and MNC is the two- or
1026     three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
1027
1028     "access-technology"
1029     A MMModemAccessTechnology value representing the generic access
1030     technology used by this mobile network, given as an unsigned
1031     integer (signature "u").
1032  */
1033  Stringmap parsed;
1034
1035  if (result.ContainsUint(kStatusProperty)) {
1036    uint32_t status = result.GetUint(kStatusProperty);
1037    // numerical values are taken from 3GPP TS 27.007 Section 7.3.
1038    static const char* const kStatusString[] = {
1039      "unknown",    // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
1040      "available",  // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
1041      "current",    // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
1042      "forbidden",  // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
1043    };
1044    parsed[kStatusProperty] = kStatusString[status];
1045  }
1046
1047  // MMModemAccessTechnology
1048  if (result.ContainsUint(kOperatorAccessTechnologyProperty)) {
1049    parsed[kTechnologyProperty] =
1050        AccessTechnologyToString(
1051            result.GetUint(kOperatorAccessTechnologyProperty));
1052  }
1053
1054  string operator_long, operator_short, operator_code;
1055  if (result.ContainsString(kOperatorLongProperty))
1056    parsed[kLongNameProperty] = result.GetString(kOperatorLongProperty);
1057  if (result.ContainsString(kOperatorShortProperty))
1058    parsed[kShortNameProperty] = result.GetString(kOperatorShortProperty);
1059  if (result.ContainsString(kOperatorCodeProperty))
1060    parsed[kNetworkIdProperty] = result.GetString(kOperatorCodeProperty);
1061
1062  // If the long name is not available but the network ID is, look up the long
1063  // name in the mobile provider database.
1064  if ((!ContainsKey(parsed, kLongNameProperty) ||
1065       parsed[kLongNameProperty].empty()) &&
1066      ContainsKey(parsed, kNetworkIdProperty)) {
1067    mobile_operator_info_->Reset();
1068    mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
1069    if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
1070        !mobile_operator_info_->operator_name().empty()) {
1071      parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
1072    }
1073  }
1074  return parsed;
1075}
1076
1077CellularBearer* CellularCapabilityUniversal::GetActiveBearer() const {
1078  return active_bearer_.get();
1079}
1080
1081string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
1082  // If we know that the modem is an E362 modem supported by the Novatel LTE
1083  // plugin, return LTE here to make sure that Chrome sees LTE as the network
1084  // technology even if the actual technology is unknown.
1085  //
1086  // This hack will cause the UI to display LTE even if the modem doesn't
1087  // support it at a given time. This might be problematic if we ever want to
1088  // support switching between access technologies (e.g. falling back to 3G
1089  // when LTE is not available).
1090  if (cellular()->mm_plugin() == kNovatelLTEMMPlugin)
1091    return kNetworkTechnologyLte;
1092
1093  // Order is important.  Return the highest speed technology
1094  // TODO(jglasgow): change shill interfaces to a capability model
1095  return AccessTechnologyToString(access_technologies_);
1096}
1097
1098string CellularCapabilityUniversal::GetRoamingStateString() const {
1099  switch (registration_state_) {
1100    case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
1101      return kRoamingStateHome;
1102    case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
1103      return kRoamingStateRoaming;
1104    default:
1105      break;
1106  }
1107  return kRoamingStateUnknown;
1108}
1109
1110// TODO(armansito): Remove this method once cromo is deprecated.
1111void CellularCapabilityUniversal::GetSignalQuality() {
1112  // ModemManager always returns the cached value, so there is no need to
1113  // trigger an update here. The true value is updated through a property
1114  // change signal.
1115}
1116
1117string CellularCapabilityUniversal::GetTypeString() const {
1118  return AccessTechnologyToTechnologyFamily(access_technologies_);
1119}
1120
1121void CellularCapabilityUniversal::OnModemPropertiesChanged(
1122    const KeyValueStore& properties,
1123    const vector<string>& /* invalidated_properties */) {
1124
1125  // Update the bearers property before the modem state property as
1126  // OnModemStateChanged may call UpdateActiveBearer, which reads the bearers
1127  // property.
1128  if (properties.ContainsRpcIdentifiers(MM_MODEM_PROPERTY_BEARERS)) {
1129    RpcIdentifiers bearers =
1130        properties.GetRpcIdentifiers(MM_MODEM_PROPERTY_BEARERS);
1131    OnBearersChanged(bearers);
1132  }
1133
1134  // This solves a bootstrapping problem: If the modem is not yet
1135  // enabled, there are no proxy objects associated with the capability
1136  // object, so modem signals like StateChanged aren't seen. By monitoring
1137  // changes to the State property via the ModemManager, we're able to
1138  // get the initialization process started, which will result in the
1139  // creation of the proxy objects.
1140  //
1141  // The first time we see the change to State (when the modem state
1142  // is Unknown), we simply update the state, and rely on the Manager to
1143  // enable the device when it is registered with the Manager. On subsequent
1144  // changes to State, we need to explicitly enable the device ourselves.
1145  if (properties.ContainsInt(MM_MODEM_PROPERTY_STATE)) {
1146    int32_t istate = properties.GetInt(MM_MODEM_PROPERTY_STATE);
1147    Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
1148    OnModemStateChanged(state);
1149  }
1150  if (properties.ContainsRpcIdentifier(MM_MODEM_PROPERTY_SIM))
1151    OnSimPathChanged(properties.GetRpcIdentifier(MM_MODEM_PROPERTY_SIM));
1152
1153  if (properties.ContainsUint32s(MM_MODEM_PROPERTY_SUPPORTEDCAPABILITIES)) {
1154    OnSupportedCapabilitesChanged(
1155        properties.GetUint32s(MM_MODEM_PROPERTY_SUPPORTEDCAPABILITIES));
1156  }
1157
1158  if (properties.ContainsUint(MM_MODEM_PROPERTY_CURRENTCAPABILITIES)) {
1159    OnModemCurrentCapabilitiesChanged(
1160        properties.GetUint(MM_MODEM_PROPERTY_CURRENTCAPABILITIES));
1161  }
1162  // not needed: MM_MODEM_PROPERTY_MAXBEARERS
1163  // not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
1164  if (properties.ContainsString(MM_MODEM_PROPERTY_MANUFACTURER)) {
1165    cellular()->set_manufacturer(
1166        properties.GetString(MM_MODEM_PROPERTY_MANUFACTURER));
1167  }
1168  if (properties.ContainsString(MM_MODEM_PROPERTY_MODEL)) {
1169    cellular()->set_model_id(properties.GetString(MM_MODEM_PROPERTY_MODEL));
1170  }
1171  if (properties.ContainsString(MM_MODEM_PROPERTY_PLUGIN)) {
1172    cellular()->set_mm_plugin(properties.GetString(MM_MODEM_PROPERTY_PLUGIN));
1173  }
1174  if (properties.ContainsString(MM_MODEM_PROPERTY_REVISION)) {
1175    OnModemRevisionChanged(properties.GetString(MM_MODEM_PROPERTY_REVISION));
1176  }
1177  // not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
1178  // not needed: MM_MODEM_PROPERTY_DEVICE
1179  // not needed: MM_MODEM_PROPERTY_DRIVER
1180  // not needed: MM_MODEM_PROPERTY_PLUGIN
1181  // not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
1182
1183  // Unlock required and SimLock
1184  bool lock_status_changed = false;
1185  if (properties.ContainsUint(MM_MODEM_PROPERTY_UNLOCKREQUIRED)) {
1186    uint32_t unlock_required =
1187        properties.GetUint(MM_MODEM_PROPERTY_UNLOCKREQUIRED);
1188    OnLockTypeChanged(static_cast<MMModemLock>(unlock_required));
1189    lock_status_changed = true;
1190  }
1191
1192  // Unlock retries
1193  if (properties.Contains(MM_MODEM_PROPERTY_UNLOCKRETRIES)) {
1194    OnLockRetriesChanged(
1195        properties.Get(MM_MODEM_PROPERTY_UNLOCKRETRIES).Get<LockRetryData>());
1196    lock_status_changed = true;
1197  }
1198
1199  if (lock_status_changed)
1200    OnSimLockStatusChanged();
1201
1202  if (properties.ContainsUint(MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES)) {
1203    OnAccessTechnologiesChanged(
1204        properties.GetUint(MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES));
1205  }
1206
1207  if (properties.Contains(MM_MODEM_PROPERTY_SIGNALQUALITY)) {
1208    SignalQuality quality =
1209        properties.Get(MM_MODEM_PROPERTY_SIGNALQUALITY).Get<SignalQuality>();
1210    OnSignalQualityChanged(std::get<0>(quality));
1211  }
1212
1213  if (properties.ContainsStrings(MM_MODEM_PROPERTY_OWNNUMBERS)) {
1214    vector<string> numbers =
1215        properties.GetStrings(MM_MODEM_PROPERTY_OWNNUMBERS);
1216    string mdn;
1217    if (numbers.size() > 0)
1218      mdn = numbers[0];
1219    OnMdnChanged(mdn);
1220  }
1221
1222  if (properties.Contains(MM_MODEM_PROPERTY_SUPPORTEDMODES)) {
1223    SupportedModes mm_supported_modes =
1224        properties.Get(MM_MODEM_PROPERTY_SUPPORTEDMODES).Get<SupportedModes>();
1225    vector<ModemModes> supported_modes;
1226    for (const auto& modes : mm_supported_modes) {
1227      supported_modes.push_back(
1228          ModemModes(std::get<0>(modes),
1229                     static_cast<MMModemMode>(std::get<1>(modes))));
1230    }
1231    OnSupportedModesChanged(supported_modes);
1232  }
1233
1234  if (properties.Contains(MM_MODEM_PROPERTY_CURRENTMODES)) {
1235    ModesData current_modes =
1236        properties.Get(MM_MODEM_PROPERTY_CURRENTMODES).Get<ModesData>();
1237    OnCurrentModesChanged(
1238        ModemModes(std::get<0>(current_modes),
1239                   static_cast<MMModemMode>(std::get<1>(current_modes))));
1240  }
1241
1242  // au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
1243  // au: MM_MODEM_PROPERTY_BANDS
1244}
1245
1246void CellularCapabilityUniversal::OnPropertiesChanged(
1247    const string& interface,
1248    const KeyValueStore& changed_properties,
1249    const vector<string>& invalidated_properties) {
1250  SLOG(this, 3) << __func__ << "(" << interface << ")";
1251  if (interface == MM_DBUS_INTERFACE_MODEM) {
1252    OnModemPropertiesChanged(changed_properties, invalidated_properties);
1253  }
1254  if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
1255    OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
1256  }
1257  if (interface == MM_DBUS_INTERFACE_SIM) {
1258    OnSimPropertiesChanged(changed_properties, invalidated_properties);
1259  }
1260}
1261
1262bool CellularCapabilityUniversal::RetriableConnectError(
1263    const Error& error) const {
1264  if (error.type() == Error::kInvalidApn)
1265    return true;
1266
1267  // ModemManager does not ever return kInvalidApn for an E362 modem (with
1268  // firmware version 1.41) supported by the Novatel LTE plugin.
1269  if ((cellular()->mm_plugin() == kNovatelLTEMMPlugin) &&
1270      (error.type() == Error::kOperationFailed)) {
1271    return true;
1272  }
1273  return false;
1274}
1275
1276void CellularCapabilityUniversal::OnNetworkModeSignal(uint32_t /*mode*/) {
1277  // TODO(petkov): Implement this.
1278  NOTIMPLEMENTED();
1279}
1280
1281bool CellularCapabilityUniversal::IsValidSimPath(const string& sim_path) const {
1282  return !sim_path.empty() && sim_path != kRootPath;
1283}
1284
1285string CellularCapabilityUniversal::NormalizeMdn(const string& mdn) const {
1286  string normalized_mdn;
1287  for (size_t i = 0; i < mdn.size(); ++i) {
1288    if (base::IsAsciiDigit(mdn[i]))
1289      normalized_mdn += mdn[i];
1290  }
1291  return normalized_mdn;
1292}
1293
1294void CellularCapabilityUniversal::OnSimPathChanged(
1295    const string& sim_path) {
1296  if (sim_path == sim_path_)
1297    return;
1298
1299  mm1::SimProxyInterface* proxy = nullptr;
1300  if (IsValidSimPath(sim_path))
1301    proxy = control_interface()->CreateSimProxy(sim_path,
1302                                                cellular()->dbus_service());
1303  sim_path_ = sim_path;
1304  sim_proxy_.reset(proxy);
1305
1306  if (!IsValidSimPath(sim_path)) {
1307    // Clear all data about the sim
1308    cellular()->set_imsi("");
1309    spn_ = "";
1310    cellular()->set_sim_present(false);
1311    OnSimIdentifierChanged("");
1312    OnOperatorIdChanged("");
1313    cellular()->home_provider_info()->Reset();
1314  } else {
1315    cellular()->set_sim_present(true);
1316    std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
1317        control_interface()->CreateDBusPropertiesProxy(
1318            sim_path, cellular()->dbus_service()));
1319    // TODO(jglasgow): convert to async interface
1320    KeyValueStore properties(properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1321    OnSimPropertiesChanged(properties, vector<string>());
1322  }
1323}
1324
1325void CellularCapabilityUniversal::OnSupportedCapabilitesChanged(
1326    const vector<uint32_t>& supported_capabilities) {
1327  supported_capabilities_ = supported_capabilities;
1328}
1329
1330void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
1331    uint32_t current_capabilities) {
1332  current_capabilities_ = current_capabilities;
1333
1334  // Only allow network scan when the modem's current capabilities support
1335  // GSM/UMTS.
1336  //
1337  // TODO(benchan): We should consider having the modem plugins in ModemManager
1338  // reporting whether network scan is supported.
1339  cellular()->set_scanning_supported(
1340      (current_capabilities & MM_MODEM_CAPABILITY_GSM_UMTS) != 0);
1341}
1342
1343void CellularCapabilityUniversal::OnMdnChanged(
1344    const string& mdn) {
1345  cellular()->set_mdn(NormalizeMdn(mdn));
1346  UpdatePendingActivationState();
1347}
1348
1349void CellularCapabilityUniversal::OnModemRevisionChanged(
1350    const string& revision) {
1351  cellular()->set_firmware_revision(revision);
1352}
1353
1354void CellularCapabilityUniversal::OnModemStateChanged(
1355    Cellular::ModemState state) {
1356  SLOG(this, 3) << __func__ << ": " << Cellular::GetModemStateString(state);
1357
1358  if (state == Cellular::kModemStateConnected) {
1359    // This assumes that ModemManager updates the Bearers list and the Bearer
1360    // properties before changing Modem state to Connected.
1361    SLOG(this, 2) << "Update active bearer.";
1362    UpdateActiveBearer();
1363  }
1364
1365  cellular()->OnModemStateChanged(state);
1366  // TODO(armansito): Move the deferred enable logic to Cellular
1367  // (See crbug.com/279499).
1368  if (!deferred_enable_modem_callback_.is_null() &&
1369      state == Cellular::kModemStateDisabled) {
1370    SLOG(this, 2) << "Enabling modem after deferring.";
1371    deferred_enable_modem_callback_.Run();
1372    deferred_enable_modem_callback_.Reset();
1373  }
1374}
1375
1376void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
1377    uint32_t access_technologies) {
1378  if (access_technologies_ != access_technologies) {
1379    const string old_type_string(GetTypeString());
1380    access_technologies_ = access_technologies;
1381    const string new_type_string(GetTypeString());
1382    if (new_type_string != old_type_string) {
1383      // TODO(jglasgow): address layering violation of emitting change
1384      // signal here for a property owned by Cellular.
1385      cellular()->adaptor()->EmitStringChanged(
1386          kTechnologyFamilyProperty, new_type_string);
1387    }
1388    if (cellular()->service().get()) {
1389      cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
1390    }
1391  }
1392}
1393
1394void CellularCapabilityUniversal::OnSupportedModesChanged(
1395    const vector<ModemModes>& supported_modes) {
1396  supported_modes_ = supported_modes;
1397}
1398
1399void CellularCapabilityUniversal::OnCurrentModesChanged(
1400    const ModemModes& current_modes) {
1401  current_modes_ = current_modes;
1402}
1403
1404void CellularCapabilityUniversal::OnBearersChanged(
1405    const RpcIdentifiers& bearers) {
1406  bearer_paths_ = bearers;
1407}
1408
1409void CellularCapabilityUniversal::OnLockRetriesChanged(
1410    const LockRetryData& lock_retries) {
1411  SLOG(this, 3) << __func__;
1412
1413  // Look for the retries left for the current lock. Try the obtain the count
1414  // that matches the current count. If no count for the current lock is
1415  // available, report the first one in the dictionary.
1416  LockRetryData::const_iterator it =
1417      lock_retries.find(sim_lock_status_.lock_type);
1418  if (it == lock_retries.end())
1419      it = lock_retries.begin();
1420  if (it != lock_retries.end())
1421    sim_lock_status_.retries_left = it->second;
1422  else
1423    // Unknown, use 999
1424    sim_lock_status_.retries_left = 999;
1425}
1426
1427void CellularCapabilityUniversal::OnLockTypeChanged(
1428    MMModemLock lock_type) {
1429  SLOG(this, 3) << __func__ << ": " << lock_type;
1430  sim_lock_status_.lock_type = lock_type;
1431
1432  // If the SIM is in a locked state |sim_lock_status_.enabled| might be false.
1433  // This is because the corresponding property 'EnabledFacilityLocks' is on
1434  // the 3GPP interface and the 3GPP interface is not available while the Modem
1435  // is in the 'LOCKED' state.
1436  if (lock_type != MM_MODEM_LOCK_NONE &&
1437      lock_type != MM_MODEM_LOCK_UNKNOWN &&
1438      !sim_lock_status_.enabled)
1439    sim_lock_status_.enabled = true;
1440}
1441
1442void CellularCapabilityUniversal::OnSimLockStatusChanged() {
1443  SLOG(this, 3) << __func__;
1444  cellular()->adaptor()->EmitKeyValueStoreChanged(
1445      kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
1446
1447  // If the SIM is currently unlocked, assume that we need to refresh
1448  // carrier information, since a locked SIM prevents shill from obtaining
1449  // the necessary data to establish a connection later (e.g. IMSI).
1450  if (IsValidSimPath(sim_path_) &&
1451      (sim_lock_status_.lock_type == MM_MODEM_LOCK_NONE ||
1452       sim_lock_status_.lock_type == MM_MODEM_LOCK_UNKNOWN)) {
1453    std::unique_ptr<DBusPropertiesProxyInterface> properties_proxy(
1454        control_interface()->CreateDBusPropertiesProxy(
1455            sim_path_, cellular()->dbus_service()));
1456    KeyValueStore properties(
1457        properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1458    OnSimPropertiesChanged(properties, vector<string>());
1459  }
1460}
1461
1462void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
1463    const KeyValueStore& properties,
1464    const vector<string>& /* invalidated_properties */) {
1465  SLOG(this, 3) << __func__;
1466  if (properties.ContainsString(MM_MODEM_MODEM3GPP_PROPERTY_IMEI))
1467    cellular()->set_imei(
1468        properties.GetString(MM_MODEM_MODEM3GPP_PROPERTY_IMEI));
1469
1470  // Handle registration state changes as a single change
1471  Stringmap::const_iterator it;
1472  string operator_code;
1473  string operator_name;
1474  it = serving_operator_.find(kOperatorCodeKey);
1475  if (it != serving_operator_.end())
1476    operator_code = it->second;
1477  it = serving_operator_.find(kOperatorNameKey);
1478  if (it != serving_operator_.end())
1479    operator_name = it->second;
1480
1481  MMModem3gppRegistrationState state = registration_state_;
1482  bool registration_changed = false;
1483  if (properties.ContainsUint(MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE)) {
1484    state = static_cast<MMModem3gppRegistrationState>(
1485        properties.GetUint(MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE));
1486    registration_changed = true;
1487  }
1488  if (properties.ContainsString(MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE)) {
1489    operator_code =
1490        properties.GetString(MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE);
1491    registration_changed = true;
1492  }
1493  if (properties.ContainsString(MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME)) {
1494    operator_name =
1495        properties.GetString(MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME);
1496    registration_changed = true;
1497  }
1498  if (registration_changed)
1499    On3GPPRegistrationChanged(state, operator_code, operator_name);
1500  if (properties.ContainsUint(MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE))
1501    On3GPPSubscriptionStateChanged(
1502        static_cast<MMModem3gppSubscriptionState>(
1503            properties.GetUint(MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE)));
1504
1505  CellularServiceRefPtr service = cellular()->service();
1506  if (service.get() &&
1507      properties.ContainsUint(MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE)) {
1508    uint32_t subscription_state =
1509        properties.GetUint(MM_MODEM_MODEM3GPP_PROPERTY_SUBSCRIPTIONSTATE);
1510    SLOG(this, 3) << __func__ << ": Subscription state = "
1511                              << subscription_state;
1512    service->out_of_credits_detector()->NotifySubscriptionStateChanged(
1513        subscription_state);
1514  }
1515
1516  if (properties.ContainsUint(MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS))
1517    OnFacilityLocksChanged(
1518        properties.GetUint(MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS));
1519}
1520
1521void CellularCapabilityUniversal::On3GPPRegistrationChanged(
1522    MMModem3gppRegistrationState state,
1523    const string& operator_code,
1524    const string& operator_name) {
1525  SLOG(this, 3) << __func__ << ": regstate=" << state
1526                            << ", opercode=" << operator_code
1527                            << ", opername=" << operator_name;
1528
1529  // While the modem is connected, if the state changed from a registered state
1530  // to a non registered state, defer the state change by 15 seconds.
1531  if (cellular()->modem_state() == Cellular::kModemStateConnected &&
1532      IsRegistered() && !IsRegisteredState(state)) {
1533    if (!registration_dropped_update_callback_.IsCancelled()) {
1534      LOG(WARNING) << "Modem reported consecutive 3GPP registration drops. "
1535                   << "Ignoring earlier notifications.";
1536      registration_dropped_update_callback_.Cancel();
1537    } else {
1538      // This is not a repeated post. So, count this instance of delayed drop
1539      // posted.
1540      modem_info()->metrics()->Notify3GPPRegistrationDelayedDropPosted();
1541    }
1542    SLOG(this, 2) << "Posted deferred registration state update";
1543    registration_dropped_update_callback_.Reset(
1544        Bind(&CellularCapabilityUniversal::Handle3GPPRegistrationChange,
1545             weak_ptr_factory_.GetWeakPtr(),
1546             state,
1547             operator_code,
1548             operator_name));
1549    cellular()->dispatcher()->PostDelayedTask(
1550        registration_dropped_update_callback_.callback(),
1551        registration_dropped_update_timeout_milliseconds_);
1552  } else {
1553    if (!registration_dropped_update_callback_.IsCancelled()) {
1554      SLOG(this, 2) << "Cancelled a deferred registration state update";
1555      registration_dropped_update_callback_.Cancel();
1556      // If we cancelled the callback here, it means we had flaky network for a
1557      // small duration.
1558      modem_info()->metrics()->Notify3GPPRegistrationDelayedDropCanceled();
1559    }
1560    Handle3GPPRegistrationChange(state, operator_code, operator_name);
1561  }
1562}
1563
1564void CellularCapabilityUniversal::Handle3GPPRegistrationChange(
1565    MMModem3gppRegistrationState updated_state,
1566    string updated_operator_code,
1567    string updated_operator_name) {
1568  // A finished callback does not qualify as a canceled callback.
1569  // We test for a canceled callback to check for outstanding callbacks.
1570  // So, explicitly cancel the callback here.
1571  registration_dropped_update_callback_.Cancel();
1572
1573  SLOG(this, 3) << __func__ << ": regstate=" << updated_state
1574                            << ", opercode=" << updated_operator_code
1575                            << ", opername=" << updated_operator_name;
1576
1577  registration_state_ = updated_state;
1578  serving_operator_[kOperatorCodeKey] = updated_operator_code;
1579  serving_operator_[kOperatorNameKey] = updated_operator_name;
1580  cellular()->serving_operator_info()->UpdateMCCMNC(updated_operator_code);
1581  cellular()->serving_operator_info()->UpdateOperatorName(
1582      updated_operator_name);
1583
1584  cellular()->HandleNewRegistrationState();
1585
1586  // If the modem registered with the network and the current ICCID is pending
1587  // activation, then reset the modem.
1588  UpdatePendingActivationState();
1589}
1590
1591void CellularCapabilityUniversal::On3GPPSubscriptionStateChanged(
1592    MMModem3gppSubscriptionState updated_state) {
1593  SLOG(this, 3) << __func__ << ": Updated subscription state = "
1594                            << updated_state;
1595
1596  // A one-to-one enum mapping.
1597  SubscriptionState new_subscription_state;
1598  switch (updated_state) {
1599    case MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN:
1600      new_subscription_state = kSubscriptionStateUnknown;
1601      break;
1602    case MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED:
1603      new_subscription_state = kSubscriptionStateProvisioned;
1604      break;
1605    case MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED:
1606      new_subscription_state = kSubscriptionStateUnprovisioned;
1607      break;
1608    case MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA:
1609      new_subscription_state = kSubscriptionStateOutOfData;
1610      break;
1611    default:
1612      LOG(ERROR) << "Unrecognized MMModem3gppSubscriptionState: "
1613                 << updated_state;
1614      new_subscription_state = kSubscriptionStateUnknown;
1615      return;
1616  }
1617  if (new_subscription_state == subscription_state_)
1618    return;
1619
1620  subscription_state_ = new_subscription_state;
1621
1622  UpdateServiceActivationState();
1623  UpdatePendingActivationState();
1624}
1625
1626void CellularCapabilityUniversal::OnModemStateChangedSignal(
1627    int32_t old_state, int32_t new_state, uint32_t reason) {
1628  Cellular::ModemState old_modem_state =
1629      static_cast<Cellular::ModemState>(old_state);
1630  Cellular::ModemState new_modem_state =
1631      static_cast<Cellular::ModemState>(new_state);
1632  SLOG(this, 3) << __func__ << "("
1633                            << Cellular::GetModemStateString(old_modem_state)
1634                            << ", "
1635                            << Cellular::GetModemStateString(new_modem_state)
1636                            << ", "
1637                            << reason << ")";
1638}
1639
1640void CellularCapabilityUniversal::OnSignalQualityChanged(uint32_t quality) {
1641  cellular()->HandleNewSignalQuality(quality);
1642}
1643
1644void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32_t locks) {
1645  bool sim_enabled = !!(locks & MM_MODEM_3GPP_FACILITY_SIM);
1646  if (sim_lock_status_.enabled != sim_enabled) {
1647    sim_lock_status_.enabled = sim_enabled;
1648    OnSimLockStatusChanged();
1649  }
1650}
1651
1652void CellularCapabilityUniversal::OnSimPropertiesChanged(
1653    const KeyValueStore& props,
1654    const vector<string>& /* invalidated_properties */) {
1655  SLOG(this, 3) << __func__;
1656  if (props.ContainsString(MM_SIM_PROPERTY_SIMIDENTIFIER))
1657    OnSimIdentifierChanged(props.GetString(MM_SIM_PROPERTY_SIMIDENTIFIER));
1658  if (props.ContainsString(MM_SIM_PROPERTY_OPERATORIDENTIFIER))
1659    OnOperatorIdChanged(props.GetString(MM_SIM_PROPERTY_OPERATORIDENTIFIER));
1660  if (props.ContainsString(MM_SIM_PROPERTY_OPERATORNAME))
1661    OnSpnChanged(props.GetString(MM_SIM_PROPERTY_OPERATORNAME));
1662  if (props.ContainsString(MM_SIM_PROPERTY_IMSI)) {
1663    string imsi = props.GetString(MM_SIM_PROPERTY_IMSI);
1664    cellular()->set_imsi(imsi);
1665    cellular()->home_provider_info()->UpdateIMSI(imsi);
1666    // We do not obtain IMSI OTA right now. Provide the value from the SIM to
1667    // serving operator as well, to aid in MVNO identification.
1668    cellular()->serving_operator_info()->UpdateIMSI(imsi);
1669  }
1670}
1671
1672void CellularCapabilityUniversal::OnSpnChanged(const std::string& spn) {
1673  spn_ = spn;
1674  cellular()->home_provider_info()->UpdateOperatorName(spn);
1675}
1676
1677void CellularCapabilityUniversal::OnSimIdentifierChanged(const string& id) {
1678  cellular()->set_sim_identifier(id);
1679  cellular()->home_provider_info()->UpdateICCID(id);
1680  // Provide ICCID to serving operator as well to aid in MVNO identification.
1681  cellular()->serving_operator_info()->UpdateICCID(id);
1682  UpdatePendingActivationState();
1683}
1684
1685void CellularCapabilityUniversal::OnOperatorIdChanged(
1686    const string& operator_id) {
1687  SLOG(this, 2) << "Operator ID = '" << operator_id << "'";
1688  cellular()->home_provider_info()->UpdateMCCMNC(operator_id);
1689}
1690
1691OutOfCreditsDetector::OOCType
1692CellularCapabilityUniversal::GetOutOfCreditsDetectionType() const {
1693  if (cellular()->mm_plugin() == kAltairLTEMMPlugin) {
1694    return OutOfCreditsDetector::OOCTypeSubscriptionState;
1695  } else {
1696    return OutOfCreditsDetector::OOCTypeNone;
1697  }
1698}
1699
1700}  // namespace shill
1701