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