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/fake_shill_manager_client.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/message_loop/message_loop.h"
10#include "base/values.h"
11#include "chromeos/chromeos_switches.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/shill_device_client.h"
14#include "chromeos/dbus/shill_profile_client.h"
15#include "chromeos/dbus/shill_property_changed_observer.h"
16#include "chromeos/dbus/shill_service_client.h"
17#include "dbus/bus.h"
18#include "dbus/message.h"
19#include "dbus/object_path.h"
20#include "dbus/values_util.h"
21#include "third_party/cros_system_api/dbus/service_constants.h"
22
23namespace chromeos {
24
25namespace {
26
27// Used to compare values for finding entries to erase in a ListValue.
28// (ListValue only implements a const_iterator version of Find).
29struct ValueEquals {
30  explicit ValueEquals(const base::Value* first) : first_(first) {}
31  bool operator()(const base::Value* second) const {
32    return first_->Equals(second);
33  }
34  const base::Value* first_;
35};
36
37// Appends string entries from |service_list_in| whose entries in ServiceClient
38// have Type |match_type| to either an active list or an inactive list
39// based on the entry's State.
40void AppendServicesForType(
41    const base::ListValue* service_list_in,
42    const char* match_type,
43    std::vector<std::string>* active_service_list_out,
44    std::vector<std::string>* inactive_service_list_out) {
45  ShillServiceClient::TestInterface* service_client =
46      DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
47  for (base::ListValue::const_iterator iter = service_list_in->begin();
48       iter != service_list_in->end(); ++iter) {
49    std::string service_path;
50    if (!(*iter)->GetAsString(&service_path))
51      continue;
52    const base::DictionaryValue* properties =
53        service_client->GetServiceProperties(service_path);
54    if (!properties) {
55      LOG(ERROR) << "Properties not found for service: " << service_path;
56      continue;
57    }
58    std::string type;
59    properties->GetString(shill::kTypeProperty, &type);
60    if (type != match_type)
61      continue;
62    std::string state;
63    properties->GetString(shill::kStateProperty, &state);
64    if (state == shill::kStateOnline ||
65        state == shill::kStateAssociation ||
66        state == shill::kStateConfiguration ||
67        state == shill::kStatePortal ||
68        state == shill::kStateReady) {
69      active_service_list_out->push_back(service_path);
70    } else {
71      inactive_service_list_out->push_back(service_path);
72    }
73  }
74}
75
76}  // namespace
77
78FakeShillManagerClient::FakeShillManagerClient()
79    : weak_ptr_factory_(this) {
80}
81
82FakeShillManagerClient::~FakeShillManagerClient() {}
83
84// ShillManagerClient overrides.
85
86void FakeShillManagerClient::Init(dbus::Bus* bus) {}
87
88void FakeShillManagerClient::AddPropertyChangedObserver(
89    ShillPropertyChangedObserver* observer) {
90  observer_list_.AddObserver(observer);
91}
92
93void FakeShillManagerClient::RemovePropertyChangedObserver(
94    ShillPropertyChangedObserver* observer) {
95  observer_list_.RemoveObserver(observer);
96}
97
98void FakeShillManagerClient::GetProperties(
99    const DictionaryValueCallback& callback) {
100  base::MessageLoop::current()->PostTask(
101      FROM_HERE, base::Bind(
102          &FakeShillManagerClient::PassStubProperties,
103          weak_ptr_factory_.GetWeakPtr(),
104          callback));
105}
106
107void FakeShillManagerClient::GetNetworksForGeolocation(
108    const DictionaryValueCallback& callback) {
109  base::MessageLoop::current()->PostTask(
110      FROM_HERE, base::Bind(
111          &FakeShillManagerClient::PassStubGeoNetworks,
112          weak_ptr_factory_.GetWeakPtr(),
113          callback));
114}
115
116void FakeShillManagerClient::SetProperty(const std::string& name,
117                                         const base::Value& value,
118                                         const base::Closure& callback,
119                                         const ErrorCallback& error_callback) {
120  stub_properties_.SetWithoutPathExpansion(name, value.DeepCopy());
121  CallNotifyObserversPropertyChanged(name, 0);
122  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
123}
124
125void FakeShillManagerClient::RequestScan(const std::string& type,
126                                         const base::Closure& callback,
127                                         const ErrorCallback& error_callback) {
128  // For Stub purposes, default to a Wifi scan.
129  std::string device_type = shill::kTypeWifi;
130  if (!type.empty())
131    device_type = type;
132  ShillDeviceClient::TestInterface* device_client =
133      DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
134  std::string device_path = device_client->GetDevicePathForType(device_type);
135  if (!device_path.empty()) {
136    device_client->SetDeviceProperty(device_path,
137                                     shill::kScanningProperty,
138                                     base::FundamentalValue(true));
139  }
140  const int kScanDurationSeconds = 3;
141  int scan_duration_seconds = kScanDurationSeconds;
142  if (!CommandLine::ForCurrentProcess()->HasSwitch(
143      chromeos::switches::kEnableStubInteractive)) {
144    scan_duration_seconds = 0;
145  }
146  base::MessageLoop::current()->PostDelayedTask(
147      FROM_HERE,
148      base::Bind(&FakeShillManagerClient::ScanCompleted,
149                 weak_ptr_factory_.GetWeakPtr(), device_path, callback),
150      base::TimeDelta::FromSeconds(scan_duration_seconds));
151}
152
153void FakeShillManagerClient::EnableTechnology(
154    const std::string& type,
155    const base::Closure& callback,
156    const ErrorCallback& error_callback) {
157  base::ListValue* enabled_list = NULL;
158  if (!stub_properties_.GetListWithoutPathExpansion(
159      shill::kEnabledTechnologiesProperty, &enabled_list)) {
160    base::MessageLoop::current()->PostTask(FROM_HERE, callback);
161    base::MessageLoop::current()->PostTask(
162        FROM_HERE,
163        base::Bind(error_callback, "StubError", "Property not found"));
164    return;
165  }
166  if (CommandLine::ForCurrentProcess()->HasSwitch(
167          chromeos::switches::kEnableStubInteractive)) {
168    const int kEnableTechnologyDelaySeconds = 3;
169    base::MessageLoop::current()->PostDelayedTask(
170        FROM_HERE,
171        base::Bind(&FakeShillManagerClient::SetTechnologyEnabled,
172                   weak_ptr_factory_.GetWeakPtr(), type, callback, true),
173        base::TimeDelta::FromSeconds(kEnableTechnologyDelaySeconds));
174  } else {
175    SetTechnologyEnabled(type, callback, true);
176  }
177}
178
179void FakeShillManagerClient::DisableTechnology(
180    const std::string& type,
181    const base::Closure& callback,
182    const ErrorCallback& error_callback) {
183  base::ListValue* enabled_list = NULL;
184  if (!stub_properties_.GetListWithoutPathExpansion(
185      shill::kEnabledTechnologiesProperty, &enabled_list)) {
186    base::MessageLoop::current()->PostTask(
187        FROM_HERE,
188        base::Bind(error_callback, "StubError", "Property not found"));
189    return;
190  }
191  if (CommandLine::ForCurrentProcess()->HasSwitch(
192          chromeos::switches::kEnableStubInteractive)) {
193    const int kDisableTechnologyDelaySeconds = 3;
194    base::MessageLoop::current()->PostDelayedTask(
195        FROM_HERE,
196        base::Bind(&FakeShillManagerClient::SetTechnologyEnabled,
197                   weak_ptr_factory_.GetWeakPtr(), type, callback, false),
198        base::TimeDelta::FromSeconds(kDisableTechnologyDelaySeconds));
199  } else {
200    SetTechnologyEnabled(type, callback, false);
201  }
202}
203
204void FakeShillManagerClient::ConfigureService(
205    const base::DictionaryValue& properties,
206    const ObjectPathCallback& callback,
207    const ErrorCallback& error_callback) {
208  ShillServiceClient::TestInterface* service_client =
209      DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
210
211  std::string guid;
212  std::string type;
213  if (!properties.GetString(shill::kGuidProperty, &guid) ||
214      !properties.GetString(shill::kTypeProperty, &type)) {
215    LOG(ERROR) << "ConfigureService requies GUID and Type to be defined";
216    // If the properties aren't filled out completely, then just return an empty
217    // object path.
218    base::MessageLoop::current()->PostTask(
219        FROM_HERE, base::Bind(callback, dbus::ObjectPath()));
220    return;
221  }
222
223  // For the purposes of this stub, we're going to assume that the GUID property
224  // is set to the service path because we don't want to re-implement Shill's
225  // property matching magic here.
226  std::string service_path = guid;
227
228  std::string ipconfig_path;
229  properties.GetString(shill::kIPConfigProperty, &ipconfig_path);
230
231  // Merge the new properties with existing properties, if any.
232  const base::DictionaryValue* existing_properties =
233      service_client->GetServiceProperties(service_path);
234  if (!existing_properties) {
235    // Add a new service to the service client stub because none exists, yet.
236    // This calls AddManagerService.
237    service_client->AddServiceWithIPConfig(service_path, guid, type,
238                                           shill::kStateIdle, ipconfig_path,
239                                           true /* visible */,
240                                           true /* watch */);
241    existing_properties = service_client->GetServiceProperties(service_path);
242  }
243
244  scoped_ptr<base::DictionaryValue> merged_properties(
245      existing_properties->DeepCopy());
246  merged_properties->MergeDictionary(&properties);
247
248  // Now set all the properties.
249  for (base::DictionaryValue::Iterator iter(*merged_properties);
250       !iter.IsAtEnd(); iter.Advance()) {
251    service_client->SetServiceProperty(service_path, iter.key(), iter.value());
252  }
253
254  // If the Profile property is set, add it to ProfileClient.
255  std::string profile_path;
256  merged_properties->GetStringWithoutPathExpansion(shill::kProfileProperty,
257                                                   &profile_path);
258  if (!profile_path.empty()) {
259    DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
260        AddService(profile_path, service_path);
261  }
262
263  base::MessageLoop::current()->PostTask(
264      FROM_HERE, base::Bind(callback, dbus::ObjectPath(service_path)));
265}
266
267void FakeShillManagerClient::ConfigureServiceForProfile(
268    const dbus::ObjectPath& profile_path,
269    const base::DictionaryValue& properties,
270    const ObjectPathCallback& callback,
271    const ErrorCallback& error_callback) {
272  std::string profile_property;
273  properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
274                                           &profile_property);
275  CHECK(profile_property == profile_path.value());
276  ConfigureService(properties, callback, error_callback);
277}
278
279
280void FakeShillManagerClient::GetService(
281    const base::DictionaryValue& properties,
282    const ObjectPathCallback& callback,
283    const ErrorCallback& error_callback) {
284  base::MessageLoop::current()->PostTask(
285      FROM_HERE, base::Bind(callback, dbus::ObjectPath()));
286}
287
288void FakeShillManagerClient::VerifyDestination(
289    const VerificationProperties& properties,
290    const BooleanCallback& callback,
291    const ErrorCallback& error_callback) {
292  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, true));
293}
294
295void FakeShillManagerClient::VerifyAndEncryptCredentials(
296    const VerificationProperties& properties,
297    const std::string& service_path,
298    const StringCallback& callback,
299    const ErrorCallback& error_callback) {
300  base::MessageLoop::current()->PostTask(
301      FROM_HERE, base::Bind(callback, "encrypted_credentials"));
302}
303
304void FakeShillManagerClient::VerifyAndEncryptData(
305    const VerificationProperties& properties,
306    const std::string& data,
307    const StringCallback& callback,
308    const ErrorCallback& error_callback) {
309  base::MessageLoop::current()->PostTask(FROM_HERE,
310                                   base::Bind(callback, "encrypted_data"));
311}
312
313void FakeShillManagerClient::ConnectToBestServices(
314    const base::Closure& callback,
315    const ErrorCallback& error_callback) {
316}
317
318ShillManagerClient::TestInterface* FakeShillManagerClient::GetTestInterface() {
319  return this;
320}
321
322// ShillManagerClient::TestInterface overrides.
323
324void FakeShillManagerClient::AddDevice(const std::string& device_path) {
325  if (GetListProperty(shill::kDevicesProperty)->AppendIfNotPresent(
326      base::Value::CreateStringValue(device_path))) {
327    CallNotifyObserversPropertyChanged(shill::kDevicesProperty, 0);
328  }
329}
330
331void FakeShillManagerClient::RemoveDevice(const std::string& device_path) {
332  base::StringValue device_path_value(device_path);
333  if (GetListProperty(shill::kDevicesProperty)->Remove(
334      device_path_value, NULL)) {
335    CallNotifyObserversPropertyChanged(shill::kDevicesProperty, 0);
336  }
337}
338
339void FakeShillManagerClient::ClearDevices() {
340  GetListProperty(shill::kDevicesProperty)->Clear();
341  CallNotifyObserversPropertyChanged(shill::kDevicesProperty, 0);
342}
343
344void FakeShillManagerClient::AddTechnology(const std::string& type,
345                                           bool enabled) {
346  if (GetListProperty(shill::kAvailableTechnologiesProperty)->
347      AppendIfNotPresent(base::Value::CreateStringValue(type))) {
348    CallNotifyObserversPropertyChanged(
349        shill::kAvailableTechnologiesProperty, 0);
350  }
351  if (enabled &&
352      GetListProperty(shill::kEnabledTechnologiesProperty)->
353      AppendIfNotPresent(base::Value::CreateStringValue(type))) {
354    CallNotifyObserversPropertyChanged(
355        shill::kEnabledTechnologiesProperty, 0);
356  }
357}
358
359void FakeShillManagerClient::RemoveTechnology(const std::string& type) {
360  base::StringValue type_value(type);
361  if (GetListProperty(shill::kAvailableTechnologiesProperty)->Remove(
362      type_value, NULL)) {
363    CallNotifyObserversPropertyChanged(
364        shill::kAvailableTechnologiesProperty, 0);
365  }
366  if (GetListProperty(shill::kEnabledTechnologiesProperty)->Remove(
367      type_value, NULL)) {
368    CallNotifyObserversPropertyChanged(
369        shill::kEnabledTechnologiesProperty, 0);
370  }
371}
372
373void FakeShillManagerClient::SetTechnologyInitializing(const std::string& type,
374                                                       bool initializing) {
375  if (initializing) {
376    if (GetListProperty(shill::kUninitializedTechnologiesProperty)->
377        AppendIfNotPresent(base::Value::CreateStringValue(type))) {
378      CallNotifyObserversPropertyChanged(
379          shill::kUninitializedTechnologiesProperty, 0);
380    }
381  } else {
382    if (GetListProperty(shill::kUninitializedTechnologiesProperty)->Remove(
383            base::StringValue(type), NULL)) {
384      CallNotifyObserversPropertyChanged(
385          shill::kUninitializedTechnologiesProperty, 0);
386    }
387  }
388}
389
390void FakeShillManagerClient::ClearProperties() {
391  stub_properties_.Clear();
392}
393
394void FakeShillManagerClient::AddManagerService(const std::string& service_path,
395                                               bool add_to_visible_list,
396                                               bool add_to_watch_list) {
397  // Always add to ServiceCompleteListProperty.
398  GetListProperty(shill::kServiceCompleteListProperty)->AppendIfNotPresent(
399      base::Value::CreateStringValue(service_path));
400  // If visible, add to Services and notify if new.
401  if (add_to_visible_list &&
402      GetListProperty(shill::kServicesProperty)->AppendIfNotPresent(
403      base::Value::CreateStringValue(service_path))) {
404    CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
405  }
406  if (add_to_watch_list)
407    AddServiceToWatchList(service_path);
408}
409
410void FakeShillManagerClient::RemoveManagerService(
411    const std::string& service_path) {
412  base::StringValue service_path_value(service_path);
413  if (GetListProperty(shill::kServicesProperty)->Remove(
414      service_path_value, NULL)) {
415    CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
416  }
417  GetListProperty(shill::kServiceCompleteListProperty)->Remove(
418      service_path_value, NULL);
419  if (GetListProperty(shill::kServiceWatchListProperty)->Remove(
420      service_path_value, NULL)) {
421    CallNotifyObserversPropertyChanged(
422        shill::kServiceWatchListProperty, 0);
423  }
424}
425
426void FakeShillManagerClient::ClearManagerServices() {
427  GetListProperty(shill::kServicesProperty)->Clear();
428  GetListProperty(shill::kServiceCompleteListProperty)->Clear();
429  GetListProperty(shill::kServiceWatchListProperty)->Clear();
430  CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
431  CallNotifyObserversPropertyChanged(shill::kServiceWatchListProperty, 0);
432}
433
434void FakeShillManagerClient::SortManagerServices() {
435  static const char* ordered_types[] = {
436    shill::kTypeEthernet,
437    shill::kTypeWifi,
438    shill::kTypeCellular,
439    shill::kTypeWimax,
440    shill::kTypeVPN
441  };
442  base::ListValue* service_list = GetListProperty(shill::kServicesProperty);
443  if (!service_list || service_list->empty())
444    return;
445  std::vector<std::string> active_services;
446  std::vector<std::string> inactive_services;
447  for (size_t i = 0; i < arraysize(ordered_types); ++i) {
448    AppendServicesForType(service_list, ordered_types[i],
449                          &active_services, &inactive_services);
450  }
451  service_list->Clear();
452  for (size_t i = 0; i < active_services.size(); ++i)
453    service_list->AppendString(active_services[i]);
454  for (size_t i = 0; i < inactive_services.size(); ++i)
455    service_list->AppendString(inactive_services[i]);
456
457  CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
458}
459
460void FakeShillManagerClient::AddGeoNetwork(
461    const std::string& technology,
462    const base::DictionaryValue& network) {
463  base::ListValue* list_value = NULL;
464  if (!stub_geo_networks_.GetListWithoutPathExpansion(
465      technology, &list_value)) {
466    list_value = new base::ListValue;
467    stub_geo_networks_.SetWithoutPathExpansion(technology, list_value);
468  }
469  list_value->Append(network.DeepCopy());
470}
471
472void FakeShillManagerClient::AddProfile(const std::string& profile_path) {
473  const char* key = shill::kProfilesProperty;
474  if (GetListProperty(key)->AppendIfNotPresent(
475          new base::StringValue(profile_path))) {
476    CallNotifyObserversPropertyChanged(key, 0);
477  }
478}
479
480void FakeShillManagerClient::AddServiceToWatchList(
481    const std::string& service_path) {
482  // Remove and insert the service, moving it to the front of the watch list.
483  GetListProperty(shill::kServiceWatchListProperty)->Remove(
484      base::StringValue(service_path), NULL);
485  GetListProperty(shill::kServiceWatchListProperty)->Insert(
486      0, base::Value::CreateStringValue(service_path));
487  CallNotifyObserversPropertyChanged(
488      shill::kServiceWatchListProperty, 0);
489}
490
491void FakeShillManagerClient::PassStubProperties(
492    const DictionaryValueCallback& callback) const {
493  scoped_ptr<base::DictionaryValue> stub_properties(
494      stub_properties_.DeepCopy());
495  // Remove disabled services from the list.
496  stub_properties->SetWithoutPathExpansion(
497      shill::kServicesProperty,
498      GetEnabledServiceList(shill::kServicesProperty));
499  stub_properties->SetWithoutPathExpansion(
500      shill::kServiceWatchListProperty,
501      GetEnabledServiceList(shill::kServiceWatchListProperty));
502  callback.Run(DBUS_METHOD_CALL_SUCCESS, *stub_properties);
503}
504
505void FakeShillManagerClient::PassStubGeoNetworks(
506    const DictionaryValueCallback& callback) const {
507  callback.Run(DBUS_METHOD_CALL_SUCCESS, stub_geo_networks_);
508}
509
510void FakeShillManagerClient::CallNotifyObserversPropertyChanged(
511    const std::string& property,
512    int delay_ms) {
513  // Avoid unnecessary delayed task if we have no observers (e.g. during
514  // initial setup).
515  if (!observer_list_.might_have_observers())
516    return;
517  if (!CommandLine::ForCurrentProcess()->HasSwitch(
518          chromeos::switches::kEnableStubInteractive)) {
519    delay_ms = 0;
520  }
521  base::MessageLoop::current()->PostDelayedTask(
522      FROM_HERE,
523      base::Bind(&FakeShillManagerClient::NotifyObserversPropertyChanged,
524                 weak_ptr_factory_.GetWeakPtr(),
525                 property),
526                 base::TimeDelta::FromMilliseconds(delay_ms));
527}
528
529void FakeShillManagerClient::NotifyObserversPropertyChanged(
530    const std::string& property) {
531  if (property == shill::kServicesProperty ||
532      property == shill::kServiceWatchListProperty) {
533    scoped_ptr<base::ListValue> services(GetEnabledServiceList(property));
534    FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
535                      observer_list_,
536                      OnPropertyChanged(property, *(services.get())));
537    return;
538  }
539  base::Value* value = NULL;
540  if (!stub_properties_.GetWithoutPathExpansion(property, &value)) {
541    LOG(ERROR) << "Notify for unknown property: " << property;
542    return;
543  }
544  FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
545                    observer_list_,
546                    OnPropertyChanged(property, *value));
547}
548
549base::ListValue* FakeShillManagerClient::GetListProperty(
550    const std::string& property) {
551  base::ListValue* list_property = NULL;
552  if (!stub_properties_.GetListWithoutPathExpansion(
553      property, &list_property)) {
554    list_property = new base::ListValue;
555    stub_properties_.SetWithoutPathExpansion(property, list_property);
556  }
557  return list_property;
558}
559
560bool FakeShillManagerClient::TechnologyEnabled(const std::string& type) const {
561  if (type == shill::kTypeVPN)
562    return true;  // VPN is always "enabled" since there is no associated device
563  bool enabled = false;
564  const base::ListValue* technologies;
565  if (stub_properties_.GetListWithoutPathExpansion(
566          shill::kEnabledTechnologiesProperty, &technologies)) {
567    base::StringValue type_value(type);
568    if (technologies->Find(type_value) != technologies->end())
569      enabled = true;
570  }
571  return enabled;
572}
573
574void FakeShillManagerClient::SetTechnologyEnabled(
575    const std::string& type,
576    const base::Closure& callback,
577    bool enabled) {
578  base::ListValue* enabled_list = NULL;
579  stub_properties_.GetListWithoutPathExpansion(
580      shill::kEnabledTechnologiesProperty, &enabled_list);
581  DCHECK(enabled_list);
582  if (enabled)
583    enabled_list->AppendIfNotPresent(new base::StringValue(type));
584  else
585    enabled_list->Remove(base::StringValue(type), NULL);
586  CallNotifyObserversPropertyChanged(
587      shill::kEnabledTechnologiesProperty, 0 /* already delayed */);
588  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
589  // May affect available services
590  CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
591  CallNotifyObserversPropertyChanged(shill::kServiceWatchListProperty, 0);
592}
593
594base::ListValue* FakeShillManagerClient::GetEnabledServiceList(
595    const std::string& property) const {
596  base::ListValue* new_service_list = new base::ListValue;
597  const base::ListValue* service_list;
598  if (stub_properties_.GetListWithoutPathExpansion(property, &service_list)) {
599    ShillServiceClient::TestInterface* service_client =
600        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
601    for (base::ListValue::const_iterator iter = service_list->begin();
602         iter != service_list->end(); ++iter) {
603      std::string service_path;
604      if (!(*iter)->GetAsString(&service_path))
605        continue;
606      const base::DictionaryValue* properties =
607          service_client->GetServiceProperties(service_path);
608      if (!properties) {
609        LOG(ERROR) << "Properties not found for service: " << service_path;
610        continue;
611      }
612      std::string name;
613      properties->GetString(shill::kNameProperty, &name);
614      std::string type;
615      properties->GetString(shill::kTypeProperty, &type);
616      if (TechnologyEnabled(type))
617        new_service_list->Append((*iter)->DeepCopy());
618    }
619  }
620  return new_service_list;
621}
622
623void FakeShillManagerClient::ScanCompleted(const std::string& device_path,
624                                           const base::Closure& callback) {
625  if (!device_path.empty()) {
626    DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()->
627        SetDeviceProperty(device_path,
628                          shill::kScanningProperty,
629                          base::FundamentalValue(false));
630  }
631  CallNotifyObserversPropertyChanged(shill::kServicesProperty, 0);
632  CallNotifyObserversPropertyChanged(shill::kServiceWatchListProperty, 0);
633  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
634}
635
636}  // namespace chromeos
637