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 
39 using std::replace_if;
40 using std::string;
41 
42 namespace shill {
43 
44 namespace Logging {
45 static auto kModuleLogScope = ScopeLogger::kWiMax;
ObjectID(WiMaxService * w)46 static string ObjectID(WiMaxService* w) { return w->GetRpcIdentifier(); }
47 }
48 
49 const char WiMaxService::kStorageNetworkId[] = "NetworkId";
50 const char WiMaxService::kNetworkIdProperty[] = "NetworkId";
51 
WiMaxService(ControlInterface * control,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager)52 WiMaxService::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 
~WiMaxService()80 WiMaxService::~WiMaxService() {}
81 
GetConnectParameters(KeyValueStore * parameters) const82 void WiMaxService::GetConnectParameters(KeyValueStore* parameters) const {
83   CHECK(parameters);
84   eap()->PopulateWiMaxProperties(parameters);
85 }
86 
GetNetworkObjectPath() const87 RpcIdentifier WiMaxService::GetNetworkObjectPath() const {
88   CHECK(proxy_.get());
89   return proxy_->path();
90 }
91 
Stop()92 void 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 
Start(WiMaxNetworkProxyInterface * proxy)107 bool 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 
IsStarted() const147 bool WiMaxService::IsStarted() const {
148   return proxy_.get();
149 }
150 
Connect(Error * error,const char * reason)151 void 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 
Disconnect(Error * error,const char * reason)183 void 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 
GetStorageIdentifier() const195 string WiMaxService::GetStorageIdentifier() const {
196   return storage_id_;
197 }
198 
GetDeviceRpcId(Error * error) const199 string 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 
IsAutoConnectable(const char ** reason) const207 bool 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 
Is8021x() const220 bool WiMaxService::Is8021x() const {
221   return true;
222 }
223 
IsVisible() const224 bool 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 
OnEapCredentialsChanged(Service::UpdateCredentialsReason reason)230 void WiMaxService::OnEapCredentialsChanged(
231     Service::UpdateCredentialsReason reason) {
232   need_passphrase_ = !eap()->IsConnectableUsingPassphrase();
233   if (reason == Service::kReasonPropertyUpdate)
234     SetHasEverConnected(false);
235   UpdateConnectable();
236 }
237 
UpdateConnectable()238 void WiMaxService::UpdateConnectable() {
239   SLOG(this, 2) << __func__ << "(started: " << IsStarted()
240                 << ", need passphrase: " << need_passphrase_ << ")";
241   SetConnectableFull(IsStarted() && !need_passphrase_);
242 }
243 
OnSignalStrengthChanged(int strength)244 void WiMaxService::OnSignalStrengthChanged(int strength) {
245   SLOG(this, 2) << __func__ << "(" << strength << ")";
246   SetStrength(strength);
247 }
248 
SetDevice(WiMaxRefPtr new_device)249 void 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 
Save(StoreInterface * storage)262 bool 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 
Unload()273 bool 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 
SetState(ConnectState state)283 void 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
ConvertIdentifierToNetworkId(uint32_t identifier)292 WiMaxNetworkId WiMaxService::ConvertIdentifierToNetworkId(uint32_t identifier) {
293   return base::StringPrintf("%08x", identifier);
294 }
295 
InitStorageIdentifier()296 void WiMaxService::InitStorageIdentifier() {
297   storage_id_ = CreateStorageIdentifier(network_id_, friendly_name());
298 }
299 
300 // static
CreateStorageIdentifier(const WiMaxNetworkId & id,const string & name)301 string 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 
ClearPassphrase()310 void WiMaxService::ClearPassphrase() {
311   SLOG(this, 2) << __func__;
312   mutable_eap()->set_password("");
313   OnEapCredentialsChanged(Service::kReasonPropertyUpdate);
314 }
315 
316 }  // namespace shill
317