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/wimax/wimax_service.h"
18
19#include <algorithm>
20
21#include <base/strings/string_number_conversions.h>
22#include <base/strings/string_util.h>
23#include <base/strings/stringprintf.h>
24#if defined(__ANDROID__)
25#include <dbus/service_constants.h>
26#else
27#include <chromeos/dbus/service_constants.h>
28#endif  // __ANDROID__
29
30#include "shill/control_interface.h"
31#include "shill/eap_credentials.h"
32#include "shill/key_value_store.h"
33#include "shill/logging.h"
34#include "shill/manager.h"
35#include "shill/store_interface.h"
36#include "shill/technology.h"
37#include "shill/wimax/wimax.h"
38
39using std::replace_if;
40using std::string;
41
42namespace shill {
43
44namespace Logging {
45static auto kModuleLogScope = ScopeLogger::kWiMax;
46static string ObjectID(WiMaxService* w) { return w->GetRpcIdentifier(); }
47}
48
49const char WiMaxService::kStorageNetworkId[] = "NetworkId";
50const char WiMaxService::kNetworkIdProperty[] = "NetworkId";
51
52WiMaxService::WiMaxService(ControlInterface* control,
53                           EventDispatcher* dispatcher,
54                           Metrics* metrics,
55                           Manager* manager)
56    : Service(control, dispatcher, metrics, manager, Technology::kWiMax),
57      need_passphrase_(true),
58      is_default_(false) {
59  PropertyStore* store = this->mutable_store();
60  // TODO(benchan): Support networks that require no user credentials or
61  // implicitly defined credentials.
62  store->RegisterBool(kPassphraseRequiredProperty, &need_passphrase_);
63  store->RegisterConstString(kNetworkIdProperty, &network_id_);
64
65  SetEapCredentials(new EapCredentials());
66
67  IgnoreParameterForConfigure(kNetworkIdProperty);
68
69  // Initialize a default storage identifier based on the service's unique
70  // name. The identifier most likely needs to be reinitialized by the caller
71  // when its components have been set.
72  InitStorageIdentifier();
73
74  // Now that |this| is a fully constructed WiMaxService, synchronize observers
75  // with our current state, and emit the appropriate change notifications.
76  // (Initial observer state may have been set in our base class.)
77  NotifyPropertyChanges();
78}
79
80WiMaxService::~WiMaxService() {}
81
82void WiMaxService::GetConnectParameters(KeyValueStore* parameters) const {
83  CHECK(parameters);
84  eap()->PopulateWiMaxProperties(parameters);
85}
86
87RpcIdentifier WiMaxService::GetNetworkObjectPath() const {
88  CHECK(proxy_.get());
89  return proxy_->path();
90}
91
92void WiMaxService::Stop() {
93  if (!IsStarted()) {
94    return;
95  }
96  LOG(INFO) << "Stopping WiMAX service: " << GetStorageIdentifier();
97  proxy_.reset();
98  SetStrength(0);
99  if (device_) {
100    device_->OnServiceStopped(this);
101    SetDevice(nullptr);
102  }
103  UpdateConnectable();
104  NotifyPropertyChanges();
105}
106
107bool WiMaxService::Start(WiMaxNetworkProxyInterface* proxy) {
108  SLOG(this, 2) << __func__;
109  CHECK(proxy);
110  std::unique_ptr<WiMaxNetworkProxyInterface> local_proxy(proxy);
111  if (IsStarted()) {
112    return true;
113  }
114  if (friendly_name().empty()) {
115    LOG(ERROR) << "Empty service name.";
116    return false;
117  }
118  Error error;
119  network_name_ = proxy->Name(&error);
120  if (error.IsFailure()) {
121    return false;
122  }
123  uint32_t identifier = proxy->Identifier(&error);
124  if (error.IsFailure()) {
125    return false;
126  }
127  WiMaxNetworkId id = ConvertIdentifierToNetworkId(identifier);
128  if (id != network_id_) {
129    LOG(ERROR) << "Network identifiers don't match: "
130               << id << " != " << network_id_;
131    return false;
132  }
133  int signal_strength = proxy->SignalStrength(&error);
134  if (error.IsFailure()) {
135    return false;
136  }
137  SetStrength(signal_strength);
138  proxy->set_signal_strength_changed_callback(
139      Bind(&WiMaxService::OnSignalStrengthChanged, Unretained(this)));
140  proxy_.reset(local_proxy.release());
141  UpdateConnectable();
142  NotifyPropertyChanges();
143  LOG(INFO) << "WiMAX service started: " << GetStorageIdentifier();
144  return true;
145}
146
147bool WiMaxService::IsStarted() const {
148  return proxy_.get();
149}
150
151void WiMaxService::Connect(Error* error, const char* reason) {
152  SLOG(this, 2) << __func__;
153  if (device_) {
154    // TODO(benchan): Populate error again after changing the way that
155    // Chrome handles Error::kAlreadyConnected situation.
156    LOG(WARNING) << "Service " << GetStorageIdentifier()
157                 << " is already being connected or already connected.";
158    return;
159  }
160  if (!connectable()) {
161    LOG(ERROR) << "Can't connect. Service " << GetStorageIdentifier()
162               << " is not connectable.";
163    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
164                          Error::GetDefaultMessage(Error::kOperationFailed));
165    return;
166  }
167  WiMaxRefPtr carrier = manager()->wimax_provider()->SelectCarrier(this);
168  if (!carrier) {
169    Error::PopulateAndLog(
170        FROM_HERE, error, Error::kNoCarrier,
171        "No suitable WiMAX device available.");
172    return;
173  }
174  Service::Connect(error, reason);
175  carrier->ConnectTo(this, error);
176  if (error->IsSuccess()) {
177    // Associate with the carrier device if the connection process has been
178    // initiated successfully.
179    SetDevice(carrier);
180  }
181}
182
183void WiMaxService::Disconnect(Error* error, const char* reason) {
184  SLOG(this, 2) << __func__;
185  if (!device_) {
186    Error::PopulateAndLog(
187        FROM_HERE, error, Error::kNotConnected, "Not connected.");
188    return;
189  }
190  Service::Disconnect(error, reason);
191  device_->DisconnectFrom(this, error);
192  SetDevice(nullptr);
193}
194
195string WiMaxService::GetStorageIdentifier() const {
196  return storage_id_;
197}
198
199string WiMaxService::GetDeviceRpcId(Error* error) const {
200  if (!device_) {
201    error->Populate(Error::kNotFound, "Not associated with a device");
202    return control_interface()->NullRPCIdentifier();
203  }
204  return device_->GetRpcIdentifier();
205}
206
207bool WiMaxService::IsAutoConnectable(const char** reason) const {
208  if (!Service::IsAutoConnectable(reason)) {
209    return false;
210  }
211  WiMaxRefPtr device = manager()->wimax_provider()->SelectCarrier(this);
212  DCHECK(device);
213  if (!device->IsIdle()) {
214    *reason = kAutoConnBusy;
215    return false;
216  }
217  return true;
218}
219
220bool WiMaxService::Is8021x() const {
221  return true;
222}
223
224bool WiMaxService::IsVisible() const {
225  // WiMAX services should be displayed only if they are in range (i.e.
226  // a corresponding network is exposed by WiMAX manager).
227  return IsStarted();
228}
229
230void WiMaxService::OnEapCredentialsChanged(
231    Service::UpdateCredentialsReason reason) {
232  need_passphrase_ = !eap()->IsConnectableUsingPassphrase();
233  if (reason == Service::kReasonPropertyUpdate)
234    SetHasEverConnected(false);
235  UpdateConnectable();
236}
237
238void WiMaxService::UpdateConnectable() {
239  SLOG(this, 2) << __func__ << "(started: " << IsStarted()
240                << ", need passphrase: " << need_passphrase_ << ")";
241  SetConnectableFull(IsStarted() && !need_passphrase_);
242}
243
244void WiMaxService::OnSignalStrengthChanged(int strength) {
245  SLOG(this, 2) << __func__ << "(" << strength << ")";
246  SetStrength(strength);
247}
248
249void WiMaxService::SetDevice(WiMaxRefPtr new_device) {
250  if (device_ == new_device)
251    return;
252  if (new_device) {
253    adaptor()->EmitRpcIdentifierChanged(kDeviceProperty,
254                                        new_device->GetRpcIdentifier());
255  } else {
256    adaptor()->EmitRpcIdentifierChanged(
257        kDeviceProperty, control_interface()->NullRPCIdentifier());
258  }
259  device_ = new_device;
260}
261
262bool WiMaxService::Save(StoreInterface* storage) {
263  SLOG(this, 2) << __func__;
264  if (!Service::Save(storage)) {
265    return false;
266  }
267  const string id = GetStorageIdentifier();
268  storage->SetString(id, kStorageNetworkId, network_id_);
269
270  return true;
271}
272
273bool WiMaxService::Unload() {
274  SLOG(this, 2) << __func__;
275  // The base method also disconnects the service.
276  Service::Unload();
277  ClearPassphrase();
278  // Notify the WiMAX provider that this service has been unloaded. If the
279  // provider releases ownership of this service, it needs to be deregistered.
280  return manager()->wimax_provider()->OnServiceUnloaded(this);
281}
282
283void WiMaxService::SetState(ConnectState state) {
284  Service::SetState(state);
285  if (!IsConnecting() && !IsConnected()) {
286    // Disassociate from any carrier device if it's not connected anymore.
287    SetDevice(nullptr);
288  }
289}
290
291// static
292WiMaxNetworkId WiMaxService::ConvertIdentifierToNetworkId(uint32_t identifier) {
293  return base::StringPrintf("%08x", identifier);
294}
295
296void WiMaxService::InitStorageIdentifier() {
297  storage_id_ = CreateStorageIdentifier(network_id_, friendly_name());
298}
299
300// static
301string WiMaxService::CreateStorageIdentifier(const WiMaxNetworkId& id,
302                                             const string& name) {
303  string storage_id = base::ToLowerASCII(
304      base::StringPrintf("%s_%s_%s",
305                         kTypeWimax, name.c_str(), id.c_str()));
306  replace_if(storage_id.begin(), storage_id.end(), &Service::IllegalChar, '_');
307  return storage_id;
308}
309
310void WiMaxService::ClearPassphrase() {
311  SLOG(this, 2) << __func__;
312  mutable_eap()->set_password("");
313  OnEapCredentialsChanged(Service::kReasonPropertyUpdate);
314}
315
316}  // namespace shill
317