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