1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/dbus/shill/fake_shill_service_client.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/task_runner.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "chromeos/dbus/shill/shill_device_client.h"
21 #include "chromeos/dbus/shill/shill_manager_client.h"
22 #include "chromeos/dbus/shill/shill_profile_client.h"
23 #include "chromeos/dbus/shill/shill_property_changed_observer.h"
24 #include "dbus/bus.h"
25 #include "dbus/message.h"
26 #include "dbus/object_path.h"
27 #include "third_party/cros_system_api/dbus/service_constants.h"
28 
29 namespace chromeos {
30 
31 namespace {
32 
CallSortManagerServices()33 void CallSortManagerServices() {
34   ShillManagerClient::Get()->GetTestInterface()->SortManagerServices(true);
35 }
36 
GetInteractiveDelay()37 base::TimeDelta GetInteractiveDelay() {
38   return ShillManagerClient::Get()->GetTestInterface()->GetInteractiveDelay();
39 }
40 
41 // Extracts the hex SSID from shill |service_properties|.
GetHexSSID(const base::Value & service_properties)42 std::string GetHexSSID(const base::Value& service_properties) {
43   const std::string* hex_ssid =
44       service_properties.FindStringKey(shill::kWifiHexSsid);
45   if (hex_ssid)
46     return *hex_ssid;
47   const std::string* ssid =
48       service_properties.FindStringKey(shill::kSSIDProperty);
49   if (ssid)
50     return base::HexEncode(ssid->c_str(), ssid->size());
51   return std::string();
52 }
53 
GetSecurityClass(const base::Value & service_properties)54 std::string GetSecurityClass(const base::Value& service_properties) {
55   // Mimics shill's WiFiProvider::GetServiceParametersFromStorage with
56   // WiFiService::ComputeSecurityClass  .
57   const std::string* security_class =
58       service_properties.FindStringKey(shill::kSecurityClassProperty);
59   const std::string* security =
60       service_properties.FindStringKey(shill::kSecurityProperty);
61   if (security_class && security && *security_class != *security) {
62     LOG(ERROR) << "Mismatch between SecurityClass " << *security_class
63                << " and Security " << *security;
64   }
65 
66   if (security_class)
67     return *security_class;
68 
69   if (security) {
70     if (*security == shill::kSecurityRsn || *security == shill::kSecurityWpa)
71       return shill::kSecurityPsk;
72     return *security;
73   }
74 
75   return shill::kSecurityNone;
76 }
77 
78 // Returns true if both |template_service_properties| and |service_properties|
79 // have the key |key| and both have the same value for it.
HaveSameValueForKey(const base::Value & template_service_properties,const base::Value & service_properties,base::StringPiece key)80 bool HaveSameValueForKey(const base::Value& template_service_properties,
81                          const base::Value& service_properties,
82                          base::StringPiece key) {
83   const base::Value* template_service_value =
84       template_service_properties.FindKey(key);
85   const base::Value* service_value = service_properties.FindKey(key);
86   return template_service_value && service_value &&
87          *template_service_value == *service_value;
88 }
89 
90 // Mimics shill's similar service matching logic. This is only invoked if
91 // |template_service_properties| and |service_properties| refer to a service of
92 // the same type.
IsSimilarService(const std::string & service_type,const base::Value & template_service_properties,const base::Value & service_properties)93 bool IsSimilarService(const std::string& service_type,
94                       const base::Value& template_service_properties,
95                       const base::Value& service_properties) {
96   if (service_type == shill::kTypeWifi) {
97     // Mimics shill's WiFiProvider::FindSimilarService.
98     return GetHexSSID(template_service_properties) ==
99                GetHexSSID(service_properties) &&
100            HaveSameValueForKey(template_service_properties, service_properties,
101                                shill::kModeProperty) &&
102            GetSecurityClass(template_service_properties) ==
103                GetSecurityClass(service_properties);
104   }
105 
106   // Assume that Ethernet / EthernetEAP services are always similar.
107   if (service_type == shill::kTypeEthernet ||
108       service_type == shill::kTypeEthernetEap) {
109     // Mimics shill's EthernetProvider::FindSimilarService.
110     return true;
111   }
112 
113   return false;
114 }
115 
116 // Properties that should be retained when a visible service is deleted from a
117 // profile, i.e. when all its configured properties are removed. This should
118 // contain properties which can be "observed", e.g. a SSID.
119 // For simplicity, these are not distinguished by service type.
120 constexpr const char* kIntrinsicServiceProperties[] = {
121     shill::kTypeProperty,
122     shill::kDeviceProperty,
123     shill::kVisibleProperty,
124     shill::kStateProperty,
125     shill::kSSIDProperty,
126     shill::kWifiHexSsid,
127     shill::kSignalStrengthProperty,
128     shill::kWifiFrequency,
129     shill::kWifiFrequencyListProperty,
130     shill::kWifiHexSsid,
131     shill::kModeProperty,
132     shill::kSecurityProperty,
133     shill::kSecurityClassProperty,
134     shill::kNetworkTechnologyProperty,
135     shill::kNameProperty,
136     shill::kProviderProperty,
137     shill::kTetheringProperty};
138 
139 }  // namespace
140 
FakeShillServiceClient()141 FakeShillServiceClient::FakeShillServiceClient() {}
142 
143 FakeShillServiceClient::~FakeShillServiceClient() = default;
144 
145 // ShillServiceClient overrides.
146 
AddPropertyChangedObserver(const dbus::ObjectPath & service_path,ShillPropertyChangedObserver * observer)147 void FakeShillServiceClient::AddPropertyChangedObserver(
148     const dbus::ObjectPath& service_path,
149     ShillPropertyChangedObserver* observer) {
150   GetObserverList(service_path).AddObserver(observer);
151 }
152 
RemovePropertyChangedObserver(const dbus::ObjectPath & service_path,ShillPropertyChangedObserver * observer)153 void FakeShillServiceClient::RemovePropertyChangedObserver(
154     const dbus::ObjectPath& service_path,
155     ShillPropertyChangedObserver* observer) {
156   GetObserverList(service_path).RemoveObserver(observer);
157 }
158 
GetProperties(const dbus::ObjectPath & service_path,DBusMethodCallback<base::Value> callback)159 void FakeShillServiceClient::GetProperties(
160     const dbus::ObjectPath& service_path,
161     DBusMethodCallback<base::Value> callback) {
162   base::Optional<base::Value> result_properties;
163   const base::Value* nested_dict = GetServiceProperties(service_path.value());
164   if (nested_dict) {
165     result_properties = nested_dict->Clone();
166     // Remove credentials that Shill wouldn't send.
167     result_properties->RemoveKey(shill::kPassphraseProperty);
168   } else {
169     // This may happen if we remove services from the list.
170     VLOG(2) << "Properties not found for: " << service_path.value();
171   }
172 
173   base::OnceClosure property_update =
174       base::BindOnce(std::move(callback), std::move(result_properties));
175   if (hold_back_service_property_updates_)
176     recorded_property_updates_.push_back(std::move(property_update));
177   else
178     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
179                                                   std::move(property_update));
180 }
181 
SetProperty(const dbus::ObjectPath & service_path,const std::string & name,const base::Value & value,base::OnceClosure callback,ErrorCallback error_callback)182 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path,
183                                          const std::string& name,
184                                          const base::Value& value,
185                                          base::OnceClosure callback,
186                                          ErrorCallback error_callback) {
187   if (!SetServiceProperty(service_path.value(), name, value)) {
188     LOG(ERROR) << "Service not found: " << service_path.value();
189     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
190     return;
191   }
192   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
193 }
194 
SetProperties(const dbus::ObjectPath & service_path,const base::Value & properties,base::OnceClosure callback,ErrorCallback error_callback)195 void FakeShillServiceClient::SetProperties(const dbus::ObjectPath& service_path,
196                                            const base::Value& properties,
197                                            base::OnceClosure callback,
198                                            ErrorCallback error_callback) {
199   for (auto iter : properties.DictItems()) {
200     if (!SetServiceProperty(service_path.value(), iter.first, iter.second)) {
201       LOG(ERROR) << "Service not found: " << service_path.value();
202       std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
203       return;
204     }
205   }
206   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
207 }
208 
ClearProperty(const dbus::ObjectPath & service_path,const std::string & name,base::OnceClosure callback,ErrorCallback error_callback)209 void FakeShillServiceClient::ClearProperty(const dbus::ObjectPath& service_path,
210                                            const std::string& name,
211                                            base::OnceClosure callback,
212                                            ErrorCallback error_callback) {
213   base::Value* dict = GetModifiableServiceProperties(
214       service_path.value(), /*create_if_missing=*/false);
215   if (!dict) {
216     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
217     return;
218   }
219   dict->RemoveKey(name);
220   // Note: Shill does not send notifications when properties are cleared.
221   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
222 }
223 
ClearProperties(const dbus::ObjectPath & service_path,const std::vector<std::string> & names,ListValueCallback callback,ErrorCallback error_callback)224 void FakeShillServiceClient::ClearProperties(
225     const dbus::ObjectPath& service_path,
226     const std::vector<std::string>& names,
227     ListValueCallback callback,
228     ErrorCallback error_callback) {
229   base::Value* dict = GetModifiableServiceProperties(
230       service_path.value(), /*create_if_missing=*/false);
231   if (!dict) {
232     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
233     return;
234   }
235 
236   base::ListValue result;
237   for (const auto& name : names) {
238     // Note: Shill does not send notifications when properties are cleared.
239     result.AppendBoolean(dict->RemoveKey(name));
240   }
241   base::ThreadTaskRunnerHandle::Get()->PostTask(
242       FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
243 }
244 
Connect(const dbus::ObjectPath & service_path,base::OnceClosure callback,ErrorCallback error_callback)245 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path,
246                                      base::OnceClosure callback,
247                                      ErrorCallback error_callback) {
248   VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value();
249   base::Value* service_properties = GetModifiableServiceProperties(
250       service_path.value(), /*create_if_missing=*/false);
251   if (!service_properties) {
252     LOG(ERROR) << "Service not found: " << service_path.value();
253     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
254     return;
255   }
256 
257   // Set any other services of the same Type to 'offline' first, before setting
258   // State to Association which will trigger sorting Manager.Services and
259   // sending an update.
260   SetOtherServicesOffline(service_path.value());
261 
262   // Clear Error.
263   service_properties->SetKey(shill::kErrorProperty, base::Value(""));
264 
265   // Set Associating.
266   base::Value associating_value(shill::kStateAssociation);
267   SetServiceProperty(service_path.value(), shill::kStateProperty,
268                      associating_value);
269 
270   // Stay Associating until the state is changed again after a delay.
271   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
272       FROM_HERE,
273       base::BindOnce(&FakeShillServiceClient::ContinueConnect,
274                      weak_ptr_factory_.GetWeakPtr(), service_path.value()),
275       GetInteractiveDelay());
276 
277   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
278 }
279 
Disconnect(const dbus::ObjectPath & service_path,base::OnceClosure callback,ErrorCallback error_callback)280 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path,
281                                         base::OnceClosure callback,
282                                         ErrorCallback error_callback) {
283   const base::Value* service = GetServiceProperties(service_path.value());
284   if (!service) {
285     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
286     return;
287   }
288   // Set Idle after a delay
289   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
290       FROM_HERE,
291       base::BindOnce(&FakeShillServiceClient::SetProperty,
292                      weak_ptr_factory_.GetWeakPtr(), service_path,
293                      shill::kStateProperty, base::Value(shill::kStateIdle),
294                      base::DoNothing::Once(), std::move(error_callback)),
295       GetInteractiveDelay());
296   std::move(callback).Run();
297 }
298 
Remove(const dbus::ObjectPath & service_path,base::OnceClosure callback,ErrorCallback error_callback)299 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path,
300                                     base::OnceClosure callback,
301                                     ErrorCallback error_callback) {
302   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
303 }
304 
CompleteCellularActivation(const dbus::ObjectPath & service_path,base::OnceClosure callback,ErrorCallback error_callback)305 void FakeShillServiceClient::CompleteCellularActivation(
306     const dbus::ObjectPath& service_path,
307     base::OnceClosure callback,
308     ErrorCallback error_callback) {
309   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
310 }
311 
GetLoadableProfileEntries(const dbus::ObjectPath & service_path,DBusMethodCallback<base::Value> callback)312 void FakeShillServiceClient::GetLoadableProfileEntries(
313     const dbus::ObjectPath& service_path,
314     DBusMethodCallback<base::Value> callback) {
315   ShillProfileClient::TestInterface* profile_client =
316       ShillProfileClient::Get()->GetTestInterface();
317   std::vector<std::string> profiles;
318   profile_client->GetProfilePathsContainingService(service_path.value(),
319                                                    &profiles);
320 
321   // Provide a dictionary with  {profile_path: service_path} entries for
322   // profile_paths that contain the service.
323   base::Value result_properties(base::Value::Type::DICTIONARY);
324   for (const auto& profile : profiles) {
325     result_properties.SetKey(profile, base::Value(service_path.value()));
326   }
327 
328   base::ThreadTaskRunnerHandle::Get()->PostTask(
329       FROM_HERE,
330       base::BindOnce(std::move(callback), std::move(result_properties)));
331 }
332 
GetWiFiPassphrase(const dbus::ObjectPath & service_path,StringCallback callback,ErrorCallback error_callback)333 void FakeShillServiceClient::GetWiFiPassphrase(
334     const dbus::ObjectPath& service_path,
335     StringCallback callback,
336     ErrorCallback error_callback) {
337   base::Value* service_properties =
338       GetModifiableServiceProperties(service_path.value(), false);
339   if (!service_properties) {
340     LOG(ERROR) << "Service not found: " << service_path.value();
341     std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
342     return;
343   }
344 
345   const std::string* passphrase =
346       service_properties->FindStringKey(shill::kPassphraseProperty);
347   std::move(callback).Run(passphrase ? *passphrase : std::string());
348 }
349 
GetTestInterface()350 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
351   return this;
352 }
353 
354 // ShillServiceClient::TestInterface overrides.
355 
AddService(const std::string & service_path,const std::string & guid,const std::string & name,const std::string & type,const std::string & state,bool visible)356 void FakeShillServiceClient::AddService(const std::string& service_path,
357                                         const std::string& guid,
358                                         const std::string& name,
359                                         const std::string& type,
360                                         const std::string& state,
361                                         bool visible) {
362   AddServiceWithIPConfig(service_path, guid, name, type, state,
363                          std::string() /* ipconfig_path */, visible);
364 }
365 
AddServiceWithIPConfig(const std::string & service_path,const std::string & guid,const std::string & name,const std::string & type,const std::string & state,const std::string & ipconfig_path,bool visible)366 void FakeShillServiceClient::AddServiceWithIPConfig(
367     const std::string& service_path,
368     const std::string& guid,
369     const std::string& name,
370     const std::string& type,
371     const std::string& state,
372     const std::string& ipconfig_path,
373     bool visible) {
374   base::Value* properties =
375       SetServiceProperties(service_path, guid, name, type, state, visible);
376 
377   if (!ipconfig_path.empty())
378     properties->SetKey(shill::kIPConfigProperty, base::Value(ipconfig_path));
379 
380   ShillManagerClient::Get()->GetTestInterface()->AddManagerService(service_path,
381                                                                    true);
382 }
383 
SetServiceProperties(const std::string & service_path,const std::string & guid,const std::string & name,const std::string & type,const std::string & state,bool visible)384 base::Value* FakeShillServiceClient::SetServiceProperties(
385     const std::string& service_path,
386     const std::string& guid,
387     const std::string& name,
388     const std::string& type,
389     const std::string& state,
390     bool visible) {
391   base::Value* properties = GetModifiableServiceProperties(service_path, true);
392   connect_behavior_.erase(service_path);
393 
394   // If |guid| is provided, set Service.GUID to that. Otherwise if a GUID is
395   // stored in a profile entry, use that. Otherwise leave it blank. Shill does
396   // not enforce a valid guid, we do that at the NetworkStateHandler layer.
397   std::string guid_to_set = guid;
398   if (guid_to_set.empty()) {
399     std::string profile_path;
400     base::Value profile_properties =
401         ShillProfileClient::Get()->GetTestInterface()->GetService(
402             service_path, &profile_path);
403     if (profile_properties.is_dict()) {
404       const std::string* profile_guid =
405           profile_properties.FindStringKey(shill::kGuidProperty);
406       if (profile_guid)
407         guid_to_set = *profile_guid;
408     }
409   }
410   if (!guid_to_set.empty())
411     properties->SetKey(shill::kGuidProperty, base::Value(guid_to_set));
412   if (type == shill::kTypeWifi) {
413     properties->SetKey(shill::kSSIDProperty, base::Value(name));
414     properties->SetKey(shill::kWifiHexSsid,
415                        base::Value(base::HexEncode(name.c_str(), name.size())));
416   }
417   properties->SetKey(shill::kNameProperty, base::Value(name));
418   std::string device_path =
419       ShillDeviceClient::Get()->GetTestInterface()->GetDevicePathForType(type);
420   properties->SetKey(shill::kDeviceProperty, base::Value(device_path));
421   properties->SetKey(shill::kTypeProperty, base::Value(type));
422   properties->SetKey(shill::kStateProperty, base::Value(state));
423   properties->SetKey(shill::kVisibleProperty, base::Value(visible));
424   if (type == shill::kTypeWifi) {
425     properties->SetKey(shill::kSecurityClassProperty,
426                        base::Value(shill::kSecurityNone));
427     properties->SetKey(shill::kModeProperty, base::Value(shill::kModeManaged));
428   }
429 
430   // Ethernet is always connectable.
431   if (type == shill::kTypeEthernet)
432     properties->SetKey(shill::kConnectableProperty, base::Value(true));
433 
434   // Cellular is always metered.
435   if (type == shill::kTypeCellular)
436     properties->SetKey(shill::kMeteredProperty, base::Value(true));
437 
438   return properties;
439 }
440 
RemoveService(const std::string & service_path)441 void FakeShillServiceClient::RemoveService(const std::string& service_path) {
442   stub_services_.RemoveKey(service_path);
443   connect_behavior_.erase(service_path);
444   ShillManagerClient::Get()->GetTestInterface()->RemoveManagerService(
445       service_path);
446 }
447 
SetServiceProperty(const std::string & service_path,const std::string & property,const base::Value & value)448 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path,
449                                                 const std::string& property,
450                                                 const base::Value& value) {
451   base::Value* dict =
452       GetModifiableServiceProperties(service_path, /*create_if_missing=*/false);
453   if (!dict)
454     return false;
455 
456   VLOG(1) << "Service.SetProperty: " << property << " = " << value
457           << " For: " << service_path;
458 
459   base::Value new_properties(base::Value::Type::DICTIONARY);
460   std::string changed_property;
461   base::CompareCase case_sensitive = base::CompareCase::SENSITIVE;
462   if (base::StartsWith(property, "Provider.", case_sensitive) ||
463       base::StartsWith(property, "OpenVPN.", case_sensitive) ||
464       base::StartsWith(property, "L2TPIPsec.", case_sensitive)) {
465     // These properties are only nested within the Provider dictionary if read
466     // from Shill. Properties that start with "Provider" need to have that
467     // stripped off, other properties are nested in the "Provider" dictionary
468     // as-is.
469     std::string key = property;
470     if (base::StartsWith(property, "Provider.", case_sensitive))
471       key = property.substr(strlen("Provider."));
472     base::Value* provider = new_properties.SetKey(
473         shill::kProviderProperty, base::Value(base::Value::Type::DICTIONARY));
474     provider->SetKey(key, value.Clone());
475     changed_property = shill::kProviderProperty;
476   } else {
477     new_properties.SetKey(property, value.Clone());
478     changed_property = property;
479   }
480 
481   // Make PSK networks connectable if 'Passphrase' is set.
482   if (changed_property == shill::kPassphraseProperty && value.is_string() &&
483       !value.GetString().empty()) {
484     new_properties.SetKey(shill::kPassphraseRequiredProperty,
485                           base::Value(false));
486     base::Value* security = dict->FindKey(shill::kSecurityClassProperty);
487     if (security && security->is_string() &&
488         security->GetString() == shill::kSecurityPsk) {
489       new_properties.SetKey(shill::kConnectableProperty, base::Value(true));
490     }
491   }
492 
493   dict->MergeDictionary(&new_properties);
494 
495   // Add or update the profile entry.
496   ShillProfileClient::TestInterface* profile_test =
497       ShillProfileClient::Get()->GetTestInterface();
498   if (property == shill::kProfileProperty) {
499     std::string profile_path;
500     if (value.GetAsString(&profile_path)) {
501       if (!profile_path.empty())
502         profile_test->AddService(profile_path, service_path);
503     } else {
504       LOG(ERROR) << "Profile value is not a String!";
505     }
506   } else {
507     const std::string* profile_path =
508         dict->FindStringKey(shill::kProfileProperty);
509     if (profile_path && !profile_path->empty())
510       profile_test->UpdateService(*profile_path, service_path);
511   }
512 
513   // Notify the Manager if the state changed (affects DefaultService).
514   if (property == shill::kStateProperty) {
515     std::string state;
516     value.GetAsString(&state);
517     ShillManagerClient::Get()->GetTestInterface()->ServiceStateChanged(
518         service_path, state);
519   }
520 
521   // If the State or Visibility changes, the sort order of service lists may
522   // change and the DefaultService property may change.
523   if (property == shill::kStateProperty ||
524       property == shill::kVisibleProperty) {
525     base::ThreadTaskRunnerHandle::Get()->PostTask(
526         FROM_HERE, base::BindOnce(&CallSortManagerServices));
527   }
528 
529   // Notifiy Chrome of the property change.
530   base::OnceClosure property_update =
531       base::BindOnce(&FakeShillServiceClient::NotifyObserversPropertyChanged,
532                      weak_ptr_factory_.GetWeakPtr(),
533                      dbus::ObjectPath(service_path), changed_property);
534   if (hold_back_service_property_updates_)
535     recorded_property_updates_.push_back(std::move(property_update));
536   else
537     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
538                                                   std::move(property_update));
539   return true;
540 }
541 
GetServiceProperties(const std::string & service_path) const542 const base::Value* FakeShillServiceClient::GetServiceProperties(
543     const std::string& service_path) const {
544   return stub_services_.FindDictKey(service_path);
545 }
546 
ClearConfiguredServiceProperties(const std::string & service_path)547 bool FakeShillServiceClient::ClearConfiguredServiceProperties(
548     const std::string& service_path) {
549   base::Value* service_dict = GetModifiableServiceProperties(
550       service_path, false /* create_if_missing */);
551   if (!service_dict)
552     return false;
553 
554   const base::Value* visible_property = service_dict->FindKeyOfType(
555       shill::kVisibleProperty, base::Value::Type::BOOLEAN);
556   const base::Value* service_type = service_dict->FindKeyOfType(
557       shill::kTypeProperty, base::Value::Type::STRING);
558   if (!visible_property || !visible_property->GetBool() || !service_type ||
559       (service_type->GetString() == shill::kTypeVPN)) {
560     stub_services_.RemoveKey(service_path);
561     RemoveService(service_path);
562     return true;
563   }
564 
565   base::Value properties_after_delete_entry(base::Value::Type::DICTIONARY);
566 
567   // Explicitly clear the profile property using SetServiceProperty so a
568   // notification is sent about that.
569   SetServiceProperty(service_path, shill::kProfileProperty,
570                      base::Value(std::string()));
571   properties_after_delete_entry.SetKey(shill::kProfileProperty,
572                                        base::Value(std::string()));
573 
574   for (const std::string& property_to_retain : kIntrinsicServiceProperties) {
575     const base::Value* value = service_dict->FindKey(property_to_retain);
576     if (!value)
577       continue;
578     properties_after_delete_entry.SetKey(property_to_retain, value->Clone());
579   }
580   stub_services_.SetKey(service_path, std::move(properties_after_delete_entry));
581   return true;
582 }
583 
FindServiceMatchingGUID(const std::string & guid)584 std::string FakeShillServiceClient::FindServiceMatchingGUID(
585     const std::string& guid) {
586   for (const auto& service_pair : stub_services_.DictItems()) {
587     const auto& service_path = service_pair.first;
588     const auto& service_properties = service_pair.second;
589 
590     const std::string* service_guid =
591         service_properties.FindStringKey(shill::kGuidProperty);
592     if (service_guid && *service_guid == guid)
593       return service_path;
594   }
595 
596   return std::string();
597 }
598 
FindSimilarService(const base::Value & template_service_properties)599 std::string FakeShillServiceClient::FindSimilarService(
600     const base::Value& template_service_properties) {
601   const std::string* template_type =
602       template_service_properties.FindStringKey(shill::kTypeProperty);
603   if (!template_type)
604     return std::string();
605 
606   for (const auto& service_pair : stub_services_.DictItems()) {
607     const auto& service_path = service_pair.first;
608     const auto& service_properties = service_pair.second;
609 
610     const std::string* service_type =
611         service_properties.FindStringKey(shill::kTypeProperty);
612     if (!service_type || *service_type != *template_type)
613       continue;
614 
615     if (IsSimilarService(*service_type, template_service_properties,
616                          service_properties)) {
617       return service_path;
618     }
619   }
620 
621   return std::string();
622 }
623 
ClearServices()624 void FakeShillServiceClient::ClearServices() {
625   ShillManagerClient::Get()->GetTestInterface()->ClearManagerServices();
626   stub_services_ = base::Value(base::Value::Type::DICTIONARY);
627   connect_behavior_.clear();
628 }
629 
SetConnectBehavior(const std::string & service_path,const base::RepeatingClosure & behavior)630 void FakeShillServiceClient::SetConnectBehavior(
631     const std::string& service_path,
632     const base::RepeatingClosure& behavior) {
633   connect_behavior_[service_path] = behavior;
634 }
635 
SetHoldBackServicePropertyUpdates(bool hold_back)636 void FakeShillServiceClient::SetHoldBackServicePropertyUpdates(bool hold_back) {
637   hold_back_service_property_updates_ = hold_back;
638   std::vector<base::OnceClosure> property_updates;
639   recorded_property_updates_.swap(property_updates);
640 
641   if (hold_back_service_property_updates_)
642     return;
643 
644   for (auto& property_update : property_updates)
645     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
646                                                   std::move(property_update));
647 }
648 
NotifyObserversPropertyChanged(const dbus::ObjectPath & service_path,const std::string & property)649 void FakeShillServiceClient::NotifyObserversPropertyChanged(
650     const dbus::ObjectPath& service_path,
651     const std::string& property) {
652   std::string path = service_path.value();
653   const base::Value* dict = GetServiceProperties(path);
654   if (!dict) {
655     LOG(ERROR) << "Notify for unknown service: " << path;
656     return;
657   }
658   const base::Value* value = dict->FindKey(property);
659   if (!value) {
660     LOG(ERROR) << "Notify for unknown property: " << path << " : " << property;
661     return;
662   }
663   for (auto& observer : GetObserverList(service_path))
664     observer.OnPropertyChanged(property, *value);
665 }
666 
GetModifiableServiceProperties(const std::string & service_path,bool create_if_missing)667 base::Value* FakeShillServiceClient::GetModifiableServiceProperties(
668     const std::string& service_path,
669     bool create_if_missing) {
670   base::Value* properties = stub_services_.FindDictKey(service_path);
671   if (!properties && create_if_missing) {
672     properties = stub_services_.SetKey(
673         service_path, base::Value(base::Value::Type::DICTIONARY));
674   }
675   return properties;
676 }
677 
678 FakeShillServiceClient::PropertyObserverList&
GetObserverList(const dbus::ObjectPath & device_path)679 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) {
680   auto iter = observer_list_.find(device_path);
681   if (iter != observer_list_.end())
682     return *(iter->second);
683   PropertyObserverList* observer_list = new PropertyObserverList();
684   observer_list_[device_path] = base::WrapUnique(observer_list);
685   return *observer_list;
686 }
687 
SetOtherServicesOffline(const std::string & service_path)688 void FakeShillServiceClient::SetOtherServicesOffline(
689     const std::string& service_path) {
690   const base::Value* service_properties = GetServiceProperties(service_path);
691   if (!service_properties) {
692     LOG(ERROR) << "Missing service: " << service_path;
693     return;
694   }
695   const std::string* service_type =
696       service_properties->FindStringKey(shill::kTypeProperty);
697   if (!service_type)
698     return;
699 
700   // Set all other services of the same type to offline (Idle).
701   for (auto iter : stub_services_.DictItems()) {
702     const std::string& path = iter.first;
703     if (path == service_path)
704       continue;
705     base::Value& properties = iter.second;
706     const std::string* type = properties.FindStringKey(shill::kTypeProperty);
707     if (!type || *type != *service_type)
708       continue;
709 
710     properties.SetKey(shill::kStateProperty, base::Value(shill::kStateIdle));
711   }
712 }
713 
SetCellularActivated(const dbus::ObjectPath & service_path,ErrorCallback error_callback)714 void FakeShillServiceClient::SetCellularActivated(
715     const dbus::ObjectPath& service_path,
716     ErrorCallback error_callback) {
717   SetProperty(service_path, shill::kActivationStateProperty,
718               base::Value(shill::kActivationStateActivated), base::DoNothing(),
719               std::move(error_callback));
720   SetProperty(service_path, shill::kConnectableProperty, base::Value(true),
721               base::DoNothing(), std::move(error_callback));
722 }
723 
ContinueConnect(const std::string & service_path)724 void FakeShillServiceClient::ContinueConnect(const std::string& service_path) {
725   VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path;
726   const base::Value* service_properties = GetServiceProperties(service_path);
727   if (!service_properties) {
728     LOG(ERROR) << "Service not found: " << service_path;
729     return;
730   }
731 
732   if (base::Contains(connect_behavior_, service_path)) {
733     const base::RepeatingClosure& custom_connect_behavior =
734         connect_behavior_[service_path];
735     VLOG(1) << "Running custom connect behavior for " << service_path;
736     custom_connect_behavior.Run();
737     return;
738   }
739 
740   // No custom connect behavior set, continue with the default connect behavior.
741   const std::string* passphrase =
742       service_properties->FindStringKey(shill::kPassphraseProperty);
743   if (passphrase && *passphrase == "failure") {
744     // Simulate a password failure.
745     SetServiceProperty(service_path, shill::kErrorProperty,
746                        base::Value(shill::kErrorBadPassphrase));
747     SetServiceProperty(service_path, shill::kStateProperty,
748                        base::Value(shill::kStateFailure));
749     base::ThreadTaskRunnerHandle::Get()->PostTask(
750         FROM_HERE,
751         base::BindOnce(
752             base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty),
753             weak_ptr_factory_.GetWeakPtr(), service_path, shill::kErrorProperty,
754             base::Value(shill::kErrorBadPassphrase)));
755   } else {
756     // Set Online.
757     VLOG(1) << "Setting state to Online " << service_path;
758     SetServiceProperty(service_path, shill::kStateProperty,
759                        base::Value(shill::kStateOnline));
760   }
761 }
762 
763 }  // namespace chromeos
764