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/strings/string_number_conversions.h"
11#include "base/strings/string_split.h"
12#include "base/strings/string_util.h"
13#include "base/values.h"
14#include "chromeos/chromeos_switches.h"
15#include "chromeos/dbus/dbus_thread_manager.h"
16#include "chromeos/dbus/shill_device_client.h"
17#include "chromeos/dbus/shill_ipconfig_client.h"
18#include "chromeos/dbus/shill_profile_client.h"
19#include "chromeos/dbus/shill_property_changed_observer.h"
20#include "chromeos/dbus/shill_service_client.h"
21#include "dbus/bus.h"
22#include "dbus/message.h"
23#include "dbus/object_path.h"
24#include "dbus/values_util.h"
25#include "third_party/cros_system_api/dbus/service_constants.h"
26
27namespace chromeos {
28
29namespace {
30
31// Used to compare values for finding entries to erase in a ListValue.
32// (ListValue only implements a const_iterator version of Find).
33struct ValueEquals {
34  explicit ValueEquals(const base::Value* first) : first_(first) {}
35  bool operator()(const base::Value* second) const {
36    return first_->Equals(second);
37  }
38  const base::Value* first_;
39};
40
41// Appends string entries from |service_list_in| whose entries in ServiceClient
42// have Type |match_type| to one of the output lists based on the entry's State.
43void AppendServicesForType(
44    const base::ListValue* service_list_in,
45    const char* match_type,
46    bool technology_enabled,
47    std::vector<std::string>* active_service_list_out,
48    std::vector<std::string>* inactive_service_list_out,
49    std::vector<std::string>* disabled_service_list_out) {
50  ShillServiceClient::TestInterface* service_client =
51      DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
52  for (base::ListValue::const_iterator iter = service_list_in->begin();
53       iter != service_list_in->end(); ++iter) {
54    std::string service_path;
55    if (!(*iter)->GetAsString(&service_path))
56      continue;
57    const base::DictionaryValue* properties =
58        service_client->GetServiceProperties(service_path);
59    if (!properties) {
60      LOG(ERROR) << "Properties not found for service: " << service_path;
61      continue;
62    }
63    std::string type;
64    properties->GetString(shill::kTypeProperty, &type);
65    if (type != match_type)
66      continue;
67    bool visible = false;
68    if (technology_enabled)
69      properties->GetBoolean(shill::kVisibleProperty, &visible);
70    if (!visible) {
71      disabled_service_list_out->push_back(service_path);
72      continue;
73    }
74    std::string state;
75    properties->GetString(shill::kStateProperty, &state);
76    if (state == shill::kStateOnline ||
77        state == shill::kStateAssociation ||
78        state == shill::kStateConfiguration ||
79        state == shill::kStatePortal ||
80        state == shill::kStateReady) {
81      active_service_list_out->push_back(service_path);
82    } else {
83      inactive_service_list_out->push_back(service_path);
84    }
85  }
86}
87
88void LogErrorCallback(const std::string& error_name,
89                      const std::string& error_message) {
90  LOG(ERROR) << error_name << ": " << error_message;
91}
92
93bool IsConnectedState(const std::string& state) {
94  return state == shill::kStateOnline || state == shill::kStatePortal ||
95         state == shill::kStateReady;
96}
97
98void UpdatePortaledWifiState(const std::string& service_path) {
99  DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface()
100      ->SetServiceProperty(service_path,
101                           shill::kStateProperty,
102                           base::StringValue(shill::kStatePortal));
103}
104
105const char* kTechnologyUnavailable = "unavailable";
106const char* kNetworkActivated = "activated";
107const char* kNetworkDisabled = "disabled";
108const char* kCellularServicePath = "/service/cellular1";
109
110}  // namespace
111
112// static
113const char FakeShillManagerClient::kFakeEthernetNetworkGuid[] = "eth1_guid";
114
115FakeShillManagerClient::FakeShillManagerClient()
116    : interactive_delay_(0),
117      weak_ptr_factory_(this) {
118  ParseCommandLineSwitch();
119}
120
121FakeShillManagerClient::~FakeShillManagerClient() {}
122
123// ShillManagerClient overrides.
124
125void FakeShillManagerClient::Init(dbus::Bus* bus) {}
126
127void FakeShillManagerClient::AddPropertyChangedObserver(
128    ShillPropertyChangedObserver* observer) {
129  observer_list_.AddObserver(observer);
130}
131
132void FakeShillManagerClient::RemovePropertyChangedObserver(
133    ShillPropertyChangedObserver* observer) {
134  observer_list_.RemoveObserver(observer);
135}
136
137void FakeShillManagerClient::GetProperties(
138    const DictionaryValueCallback& callback) {
139  DVLOG(1) << "Manager.GetProperties";
140  base::MessageLoop::current()->PostTask(
141      FROM_HERE, base::Bind(
142          &FakeShillManagerClient::PassStubProperties,
143          weak_ptr_factory_.GetWeakPtr(),
144          callback));
145}
146
147void FakeShillManagerClient::GetNetworksForGeolocation(
148    const DictionaryValueCallback& callback) {
149  base::MessageLoop::current()->PostTask(
150      FROM_HERE, base::Bind(
151          &FakeShillManagerClient::PassStubGeoNetworks,
152          weak_ptr_factory_.GetWeakPtr(),
153          callback));
154}
155
156void FakeShillManagerClient::SetProperty(const std::string& name,
157                                         const base::Value& value,
158                                         const base::Closure& callback,
159                                         const ErrorCallback& error_callback) {
160  DVLOG(2) << "SetProperty: " << name;
161  stub_properties_.SetWithoutPathExpansion(name, value.DeepCopy());
162  CallNotifyObserversPropertyChanged(name);
163  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
164}
165
166void FakeShillManagerClient::RequestScan(const std::string& type,
167                                         const base::Closure& callback,
168                                         const ErrorCallback& error_callback) {
169  // For Stub purposes, default to a Wifi scan.
170  std::string device_type = shill::kTypeWifi;
171  if (!type.empty())
172    device_type = type;
173  ShillDeviceClient::TestInterface* device_client =
174      DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
175  std::string device_path = device_client->GetDevicePathForType(device_type);
176  if (!device_path.empty()) {
177    device_client->SetDeviceProperty(
178        device_path, shill::kScanningProperty, base::FundamentalValue(true));
179  }
180  base::MessageLoop::current()->PostDelayedTask(
181      FROM_HERE,
182      base::Bind(&FakeShillManagerClient::ScanCompleted,
183                 weak_ptr_factory_.GetWeakPtr(),
184                 device_path,
185                 callback),
186      base::TimeDelta::FromSeconds(interactive_delay_));
187}
188
189void FakeShillManagerClient::EnableTechnology(
190    const std::string& type,
191    const base::Closure& callback,
192    const ErrorCallback& error_callback) {
193  base::ListValue* enabled_list = NULL;
194  if (!stub_properties_.GetListWithoutPathExpansion(
195          shill::kAvailableTechnologiesProperty, &enabled_list)) {
196    base::MessageLoop::current()->PostTask(FROM_HERE, callback);
197    base::MessageLoop::current()->PostTask(
198        FROM_HERE,
199        base::Bind(error_callback, "StubError", "Property not found"));
200    return;
201  }
202  base::MessageLoop::current()->PostDelayedTask(
203      FROM_HERE,
204      base::Bind(&FakeShillManagerClient::SetTechnologyEnabled,
205                 weak_ptr_factory_.GetWeakPtr(),
206                 type,
207                 callback,
208                 true),
209      base::TimeDelta::FromSeconds(interactive_delay_));
210}
211
212void FakeShillManagerClient::DisableTechnology(
213    const std::string& type,
214    const base::Closure& callback,
215    const ErrorCallback& error_callback) {
216  base::ListValue* enabled_list = NULL;
217  if (!stub_properties_.GetListWithoutPathExpansion(
218          shill::kAvailableTechnologiesProperty, &enabled_list)) {
219    base::MessageLoop::current()->PostTask(
220        FROM_HERE,
221        base::Bind(error_callback, "StubError", "Property not found"));
222    return;
223  }
224  base::MessageLoop::current()->PostDelayedTask(
225      FROM_HERE,
226      base::Bind(&FakeShillManagerClient::SetTechnologyEnabled,
227                 weak_ptr_factory_.GetWeakPtr(),
228                 type,
229                 callback,
230                 false),
231      base::TimeDelta::FromSeconds(interactive_delay_));
232}
233
234void FakeShillManagerClient::ConfigureService(
235    const base::DictionaryValue& properties,
236    const ObjectPathCallback& callback,
237    const ErrorCallback& error_callback) {
238  ShillServiceClient::TestInterface* service_client =
239      DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
240
241  std::string guid;
242  std::string type;
243  if (!properties.GetString(shill::kGuidProperty, &guid) ||
244      !properties.GetString(shill::kTypeProperty, &type)) {
245    LOG(ERROR) << "ConfigureService requires GUID and Type to be defined";
246    // If the properties aren't filled out completely, then just return an empty
247    // object path.
248    base::MessageLoop::current()->PostTask(
249        FROM_HERE, base::Bind(callback, dbus::ObjectPath()));
250    return;
251  }
252
253  // For the purposes of this stub, we're going to assume that the GUID property
254  // is set to the service path because we don't want to re-implement Shill's
255  // property matching magic here.
256  std::string service_path = guid;
257
258  std::string ipconfig_path;
259  properties.GetString(shill::kIPConfigProperty, &ipconfig_path);
260
261  // Merge the new properties with existing properties, if any.
262  const base::DictionaryValue* existing_properties =
263      service_client->GetServiceProperties(service_path);
264  if (!existing_properties) {
265    // Add a new service to the service client stub because none exists, yet.
266    // This calls AddManagerService.
267    service_client->AddServiceWithIPConfig(service_path,
268                                           guid /* guid */,
269                                           guid /* name */,
270                                           type,
271                                           shill::kStateIdle,
272                                           ipconfig_path,
273                                           true /* visible */);
274    existing_properties = service_client->GetServiceProperties(service_path);
275  }
276
277  scoped_ptr<base::DictionaryValue> merged_properties(
278      existing_properties->DeepCopy());
279  merged_properties->MergeDictionary(&properties);
280
281  // Now set all the properties.
282  for (base::DictionaryValue::Iterator iter(*merged_properties);
283       !iter.IsAtEnd(); iter.Advance()) {
284    service_client->SetServiceProperty(service_path, iter.key(), iter.value());
285  }
286
287  // If the Profile property is set, add it to ProfileClient.
288  std::string profile_path;
289  merged_properties->GetStringWithoutPathExpansion(shill::kProfileProperty,
290                                                   &profile_path);
291  if (!profile_path.empty()) {
292    DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
293        AddService(profile_path, service_path);
294  }
295
296  base::MessageLoop::current()->PostTask(
297      FROM_HERE, base::Bind(callback, dbus::ObjectPath(service_path)));
298}
299
300void FakeShillManagerClient::ConfigureServiceForProfile(
301    const dbus::ObjectPath& profile_path,
302    const base::DictionaryValue& properties,
303    const ObjectPathCallback& callback,
304    const ErrorCallback& error_callback) {
305  std::string profile_property;
306  properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
307                                           &profile_property);
308  CHECK(profile_property == profile_path.value());
309  ConfigureService(properties, callback, error_callback);
310}
311
312
313void FakeShillManagerClient::GetService(
314    const base::DictionaryValue& properties,
315    const ObjectPathCallback& callback,
316    const ErrorCallback& error_callback) {
317  base::MessageLoop::current()->PostTask(
318      FROM_HERE, base::Bind(callback, dbus::ObjectPath()));
319}
320
321void FakeShillManagerClient::VerifyDestination(
322    const VerificationProperties& properties,
323    const BooleanCallback& callback,
324    const ErrorCallback& error_callback) {
325  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, true));
326}
327
328void FakeShillManagerClient::VerifyAndEncryptCredentials(
329    const VerificationProperties& properties,
330    const std::string& service_path,
331    const StringCallback& callback,
332    const ErrorCallback& error_callback) {
333  base::MessageLoop::current()->PostTask(
334      FROM_HERE, base::Bind(callback, "encrypted_credentials"));
335}
336
337void FakeShillManagerClient::VerifyAndEncryptData(
338    const VerificationProperties& properties,
339    const std::string& data,
340    const StringCallback& callback,
341    const ErrorCallback& error_callback) {
342  base::MessageLoop::current()->PostTask(
343      FROM_HERE, base::Bind(callback, "encrypted_data"));
344}
345
346void FakeShillManagerClient::ConnectToBestServices(
347    const base::Closure& callback,
348    const ErrorCallback& error_callback) {
349  if (best_service_.empty()) {
350    VLOG(1) << "No 'best' service set.";
351    return;
352  }
353
354  DBusThreadManager::Get()->GetShillServiceClient()->Connect(
355      dbus::ObjectPath(best_service_), callback, error_callback);
356}
357
358void FakeShillManagerClient::AddWakeOnPacketConnection(
359    const net::IPEndPoint& ip_endpoint,
360    const base::Closure& callback,
361    const ErrorCallback& error_callback) {
362}
363
364void FakeShillManagerClient::RemoveWakeOnPacketConnection(
365    const net::IPEndPoint& ip_endpoint,
366    const base::Closure& callback,
367    const ErrorCallback& error_callback) {
368}
369
370void FakeShillManagerClient::RemoveAllWakeOnPacketConnections(
371    const base::Closure& callback,
372    const ErrorCallback& error_callback) {
373}
374
375ShillManagerClient::TestInterface* FakeShillManagerClient::GetTestInterface() {
376  return this;
377}
378
379// ShillManagerClient::TestInterface overrides.
380
381void FakeShillManagerClient::AddDevice(const std::string& device_path) {
382  if (GetListProperty(shill::kDevicesProperty)
383          ->AppendIfNotPresent(new base::StringValue(device_path))) {
384    CallNotifyObserversPropertyChanged(shill::kDevicesProperty);
385  }
386}
387
388void FakeShillManagerClient::RemoveDevice(const std::string& device_path) {
389  base::StringValue device_path_value(device_path);
390  if (GetListProperty(shill::kDevicesProperty)->Remove(
391      device_path_value, NULL)) {
392    CallNotifyObserversPropertyChanged(shill::kDevicesProperty);
393  }
394}
395
396void FakeShillManagerClient::ClearDevices() {
397  GetListProperty(shill::kDevicesProperty)->Clear();
398  CallNotifyObserversPropertyChanged(shill::kDevicesProperty);
399}
400
401void FakeShillManagerClient::AddTechnology(const std::string& type,
402                                           bool enabled) {
403  if (GetListProperty(shill::kAvailableTechnologiesProperty)
404          ->AppendIfNotPresent(new base::StringValue(type))) {
405    CallNotifyObserversPropertyChanged(
406        shill::kAvailableTechnologiesProperty);
407  }
408  if (enabled &&
409      GetListProperty(shill::kEnabledTechnologiesProperty)
410          ->AppendIfNotPresent(new base::StringValue(type))) {
411    CallNotifyObserversPropertyChanged(
412        shill::kEnabledTechnologiesProperty);
413  }
414}
415
416void FakeShillManagerClient::RemoveTechnology(const std::string& type) {
417  base::StringValue type_value(type);
418  if (GetListProperty(shill::kAvailableTechnologiesProperty)->Remove(
419      type_value, NULL)) {
420    CallNotifyObserversPropertyChanged(
421        shill::kAvailableTechnologiesProperty);
422  }
423  if (GetListProperty(shill::kEnabledTechnologiesProperty)->Remove(
424      type_value, NULL)) {
425    CallNotifyObserversPropertyChanged(
426        shill::kEnabledTechnologiesProperty);
427  }
428}
429
430void FakeShillManagerClient::SetTechnologyInitializing(const std::string& type,
431                                                       bool initializing) {
432  if (initializing) {
433    if (GetListProperty(shill::kUninitializedTechnologiesProperty)
434            ->AppendIfNotPresent(new base::StringValue(type))) {
435      CallNotifyObserversPropertyChanged(
436          shill::kUninitializedTechnologiesProperty);
437    }
438  } else {
439    if (GetListProperty(shill::kUninitializedTechnologiesProperty)->Remove(
440            base::StringValue(type), NULL)) {
441      CallNotifyObserversPropertyChanged(
442          shill::kUninitializedTechnologiesProperty);
443    }
444  }
445}
446
447void FakeShillManagerClient::AddGeoNetwork(
448    const std::string& technology,
449    const base::DictionaryValue& network) {
450  base::ListValue* list_value = NULL;
451  if (!stub_geo_networks_.GetListWithoutPathExpansion(technology,
452                                                      &list_value)) {
453    list_value = new base::ListValue;
454    stub_geo_networks_.SetWithoutPathExpansion(technology, list_value);
455  }
456  list_value->Append(network.DeepCopy());
457}
458
459void FakeShillManagerClient::AddProfile(const std::string& profile_path) {
460  const char* key = shill::kProfilesProperty;
461  if (GetListProperty(key)
462          ->AppendIfNotPresent(new base::StringValue(profile_path))) {
463    CallNotifyObserversPropertyChanged(key);
464  }
465}
466
467void FakeShillManagerClient::ClearProperties() {
468  stub_properties_.Clear();
469}
470
471void FakeShillManagerClient::SetManagerProperty(const std::string& key,
472                                                const base::Value& value) {
473  SetProperty(key, value,
474              base::Bind(&base::DoNothing), base::Bind(&LogErrorCallback));
475}
476
477void FakeShillManagerClient::AddManagerService(
478    const std::string& service_path,
479    bool notify_observers) {
480  DVLOG(2) << "AddManagerService: " << service_path;
481  GetListProperty(shill::kServiceCompleteListProperty)
482      ->AppendIfNotPresent(new base::StringValue(service_path));
483  SortManagerServices(false);
484  if (notify_observers)
485    CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
486}
487
488void FakeShillManagerClient::RemoveManagerService(
489    const std::string& service_path) {
490  DVLOG(2) << "RemoveManagerService: " << service_path;
491  base::StringValue service_path_value(service_path);
492  GetListProperty(shill::kServiceCompleteListProperty)->Remove(
493      service_path_value, NULL);
494  CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
495}
496
497void FakeShillManagerClient::ClearManagerServices() {
498  DVLOG(1) << "ClearManagerServices";
499  GetListProperty(shill::kServiceCompleteListProperty)->Clear();
500  CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
501}
502
503void FakeShillManagerClient::ServiceStateChanged(
504    const std::string& service_path,
505    const std::string& state) {
506  if (service_path == default_service_ && !IsConnectedState(state)) {
507    // Default service is no longer connected; clear.
508    default_service_.clear();
509    base::StringValue default_service_value(default_service_);
510    SetManagerProperty(shill::kDefaultServiceProperty, default_service_value);
511  }
512}
513
514void FakeShillManagerClient::SortManagerServices(bool notify) {
515  DVLOG(1) << "SortManagerServices";
516  static const char* ordered_types[] = {shill::kTypeEthernet,
517                                        shill::kTypeEthernetEap,
518                                        shill::kTypeWifi,
519                                        shill::kTypeCellular,
520                                        shill::kTypeWimax,
521                                        shill::kTypeVPN};
522
523  base::ListValue* complete_list =
524      GetListProperty(shill::kServiceCompleteListProperty);
525  if (complete_list->empty())
526    return;
527  scoped_ptr<base::ListValue> prev_complete_list(complete_list->DeepCopy());
528
529  std::vector<std::string> active_services;
530  std::vector<std::string> inactive_services;
531  std::vector<std::string> disabled_services;
532  for (size_t i = 0; i < arraysize(ordered_types); ++i) {
533    AppendServicesForType(complete_list,
534                          ordered_types[i],
535                          TechnologyEnabled(ordered_types[i]),
536                          &active_services,
537                          &inactive_services,
538                          &disabled_services);
539  }
540  complete_list->Clear();
541  for (size_t i = 0; i < active_services.size(); ++i)
542    complete_list->AppendString(active_services[i]);
543  for (size_t i = 0; i < inactive_services.size(); ++i)
544    complete_list->AppendString(inactive_services[i]);
545  for (size_t i = 0; i < disabled_services.size(); ++i)
546    complete_list->AppendString(disabled_services[i]);
547
548  if (notify && !complete_list->Equals(prev_complete_list.get()))
549    CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
550
551  // Set the first active service as the Default service.
552  std::string new_default_service;
553  if (!active_services.empty()) {
554    ShillServiceClient::TestInterface* service_client =
555        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
556    std::string service_path = active_services[0];
557    const base::DictionaryValue* properties =
558        service_client->GetServiceProperties(service_path);
559    if (!properties) {
560      LOG(ERROR) << "Properties not found for service: " << service_path;
561    } else {
562      std::string state;
563      properties->GetString(shill::kStateProperty, &state);
564      if (IsConnectedState(state))
565        new_default_service = service_path;
566    }
567  }
568  if (default_service_ != new_default_service) {
569    default_service_ = new_default_service;
570    base::StringValue default_service_value(default_service_);
571    SetManagerProperty(shill::kDefaultServiceProperty, default_service_value);
572  }
573}
574
575int FakeShillManagerClient::GetInteractiveDelay() const {
576  return interactive_delay_;
577}
578
579void FakeShillManagerClient::SetBestServiceToConnect(
580    const std::string& service_path) {
581  best_service_ = service_path;
582}
583
584void FakeShillManagerClient::SetupDefaultEnvironment() {
585  // Bail out from setup if there is no message loop. This will be the common
586  // case for tests that are not testing Shill.
587  if (!base::MessageLoop::current())
588    return;
589
590  DBusThreadManager* dbus_manager = DBusThreadManager::Get();
591  ShillServiceClient::TestInterface* services =
592      dbus_manager->GetShillServiceClient()->GetTestInterface();
593  DCHECK(services);
594  ShillProfileClient::TestInterface* profiles =
595      dbus_manager->GetShillProfileClient()->GetTestInterface();
596  DCHECK(profiles);
597  ShillDeviceClient::TestInterface* devices =
598      dbus_manager->GetShillDeviceClient()->GetTestInterface();
599  DCHECK(devices);
600  ShillIPConfigClient::TestInterface* ip_configs =
601      dbus_manager->GetShillIPConfigClient()->GetTestInterface();
602  DCHECK(ip_configs);
603
604  const std::string shared_profile = ShillProfileClient::GetSharedProfilePath();
605  profiles->AddProfile(shared_profile, std::string());
606
607  const bool add_to_visible = true;
608
609  // IPConfigs
610  base::DictionaryValue ipconfig_v4_dictionary;
611  ipconfig_v4_dictionary.SetStringWithoutPathExpansion(
612      shill::kAddressProperty, "0.0.0.0");
613  ipconfig_v4_dictionary.SetStringWithoutPathExpansion(
614      shill::kGatewayProperty, "0.0.0.1");
615  ipconfig_v4_dictionary.SetIntegerWithoutPathExpansion(
616      shill::kPrefixlenProperty, 0);
617  ipconfig_v4_dictionary.SetStringWithoutPathExpansion(
618      shill::kMethodProperty, shill::kTypeIPv4);
619  ip_configs->AddIPConfig("ipconfig_v4_path", ipconfig_v4_dictionary);
620  base::DictionaryValue ipconfig_v6_dictionary;
621  ipconfig_v6_dictionary.SetStringWithoutPathExpansion(
622      shill::kAddressProperty, "0:0:0:0:0:0:0:0");
623  ipconfig_v6_dictionary.SetStringWithoutPathExpansion(
624      shill::kMethodProperty, shill::kTypeIPv6);
625  ip_configs->AddIPConfig("ipconfig_v6_path", ipconfig_v6_dictionary);
626
627  bool enabled;
628  std::string state;
629
630  // Ethernet
631  state = GetInitialStateForType(shill::kTypeEthernet, &enabled);
632  if (state == shill::kStateOnline) {
633    AddTechnology(shill::kTypeEthernet, enabled);
634    devices->AddDevice(
635        "/device/eth1", shill::kTypeEthernet, "stub_eth_device1");
636    devices->SetDeviceProperty("/device/eth1",
637                               shill::kAddressProperty,
638                               base::StringValue("0123456789ab"));
639    base::ListValue eth_ip_configs;
640    eth_ip_configs.AppendString("ipconfig_v4_path");
641    eth_ip_configs.AppendString("ipconfig_v6_path");
642    devices->SetDeviceProperty("/device/eth1",
643                               shill::kIPConfigsProperty,
644                               eth_ip_configs);
645    const std::string kFakeEthernetNetworkPath = "/service/eth1";
646    services->AddService(kFakeEthernetNetworkPath,
647                         kFakeEthernetNetworkGuid,
648                         "eth1" /* name */,
649                         shill::kTypeEthernet,
650                         state,
651                         add_to_visible);
652    profiles->AddService(shared_profile, kFakeEthernetNetworkPath);
653  }
654
655  // Wifi
656  state = GetInitialStateForType(shill::kTypeWifi, &enabled);
657  if (state != kTechnologyUnavailable) {
658    bool portaled = false;
659    if (state == shill::kStatePortal) {
660      portaled = true;
661      state = shill::kStateIdle;
662    }
663    AddTechnology(shill::kTypeWifi, enabled);
664    devices->AddDevice("/device/wifi1", shill::kTypeWifi, "stub_wifi_device1");
665    devices->SetDeviceProperty("/device/wifi1",
666                               shill::kAddressProperty,
667                               base::StringValue("23456789abc"));
668    base::ListValue wifi_ip_configs;
669    wifi_ip_configs.AppendString("ipconfig_v4_path");
670    wifi_ip_configs.AppendString("ipconfig_v6_path");
671    devices->SetDeviceProperty("/device/wifi1",
672                               shill::kIPConfigsProperty,
673                               wifi_ip_configs);
674
675    const std::string kWifi1Path = "/service/wifi1";
676    services->AddService(kWifi1Path,
677                         "wifi1_guid",
678                         "wifi1" /* name */,
679                         shill::kTypeWifi,
680                         state,
681                         add_to_visible);
682    services->SetServiceProperty(kWifi1Path,
683                                 shill::kSecurityProperty,
684                                 base::StringValue(shill::kSecurityWep));
685    services->SetServiceProperty(kWifi1Path,
686                                 shill::kConnectableProperty,
687                                 base::FundamentalValue(true));
688    profiles->AddService(shared_profile, kWifi1Path);
689
690    const std::string kWifi2Path = "/service/wifi2";
691    services->AddService(kWifi2Path,
692                         "wifi2_PSK_guid",
693                         "wifi2_PSK" /* name */,
694                         shill::kTypeWifi,
695                         shill::kStateIdle,
696                         add_to_visible);
697    services->SetServiceProperty(kWifi2Path,
698                                 shill::kSecurityProperty,
699                                 base::StringValue(shill::kSecurityPsk));
700
701    base::FundamentalValue strength_value(80);
702    services->SetServiceProperty(
703        kWifi2Path, shill::kSignalStrengthProperty, strength_value);
704    profiles->AddService(shared_profile, kWifi2Path);
705
706    if (portaled) {
707      const std::string kPortaledWifiPath = "/service/portaled_wifi";
708      services->AddService(kPortaledWifiPath,
709                           "portaled_wifi_guid",
710                           "Portaled Wifi" /* name */,
711                           shill::kTypeWifi,
712                           shill::kStatePortal,
713                           add_to_visible);
714      services->SetServiceProperty(kPortaledWifiPath,
715                                   shill::kSecurityProperty,
716                                   base::StringValue(shill::kSecurityNone));
717      services->SetConnectBehavior(kPortaledWifiPath,
718                                   base::Bind(&UpdatePortaledWifiState,
719                                              "portaled_wifi"));
720      services->SetServiceProperty(kPortaledWifiPath,
721                                   shill::kConnectableProperty,
722                                   base::FundamentalValue(true));
723      profiles->AddService(shared_profile, kPortaledWifiPath);
724    }
725  }
726
727  // Wimax
728  state = GetInitialStateForType(shill::kTypeWimax, &enabled);
729  if (state != kTechnologyUnavailable) {
730    AddTechnology(shill::kTypeWimax, enabled);
731    devices->AddDevice(
732        "/device/wimax1", shill::kTypeWimax, "stub_wimax_device1");
733
734    services->AddService("/service/wimax1",
735                         "wimax1_guid",
736                         "wimax1" /* name */,
737                         shill::kTypeWimax,
738                         state,
739                         add_to_visible);
740    services->SetServiceProperty("/service/wimax1",
741                                 shill::kConnectableProperty,
742                                 base::FundamentalValue(true));
743    base::FundamentalValue strength_value(80);
744    services->SetServiceProperty(
745        "/service/wimax1", shill::kSignalStrengthProperty, strength_value);
746    base::StringValue identity_value("test.identity");
747    services->SetServiceProperty(
748        "/service/wimax1", shill::kEapIdentityProperty, identity_value);
749  }
750
751  // Cellular
752  state = GetInitialStateForType(shill::kTypeCellular, &enabled);
753  if (state != kTechnologyUnavailable) {
754    bool activated = false;
755    if (state == kNetworkActivated) {
756      activated = true;
757      state = shill::kStateIdle;
758    }
759    AddTechnology(shill::kTypeCellular, enabled);
760    devices->AddDevice(
761        "/device/cellular1", shill::kTypeCellular, "stub_cellular_device1");
762    devices->SetDeviceProperty("/device/cellular1",
763                               shill::kCarrierProperty,
764                               base::StringValue(shill::kCarrierSprint));
765    base::ListValue carrier_list;
766    carrier_list.AppendString(shill::kCarrierSprint);
767    carrier_list.AppendString(shill::kCarrierGenericUMTS);
768    devices->SetDeviceProperty("/device/cellular1",
769                               shill::kSupportedCarriersProperty,
770                               carrier_list);
771
772    services->AddService(kCellularServicePath,
773                         "cellular1_guid",
774                         "cellular1" /* name */,
775                         shill::kTypeCellular,
776                         state,
777                         add_to_visible);
778    base::StringValue technology_value(shill::kNetworkTechnologyGsm);
779    services->SetServiceProperty(kCellularServicePath,
780                                 shill::kNetworkTechnologyProperty,
781                                 technology_value);
782
783    if (activated) {
784      services->SetServiceProperty(
785          kCellularServicePath,
786          shill::kActivationStateProperty,
787          base::StringValue(shill::kActivationStateActivated));
788      services->SetServiceProperty(kCellularServicePath,
789                                   shill::kConnectableProperty,
790                                   base::FundamentalValue(true));
791    } else {
792      services->SetServiceProperty(
793          kCellularServicePath,
794          shill::kActivationStateProperty,
795          base::StringValue(shill::kActivationStateNotActivated));
796    }
797
798    services->SetServiceProperty(kCellularServicePath,
799                                 shill::kRoamingStateProperty,
800                                 base::StringValue(shill::kRoamingStateHome));
801  }
802
803  // VPN
804  state = GetInitialStateForType(shill::kTypeVPN, &enabled);
805  if (state != kTechnologyUnavailable) {
806    // Set the "Provider" dictionary properties. Note: when setting these in
807    // Shill, "Provider.Type", etc keys are used, but when reading the values
808    // "Provider" . "Type", etc keys are used. Here we are setting the values
809    // that will be read (by the UI, tests, etc).
810    base::DictionaryValue provider_properties_openvpn;
811    provider_properties_openvpn.SetString(shill::kTypeProperty,
812                                          shill::kProviderOpenVpn);
813    provider_properties_openvpn.SetString(shill::kHostProperty, "vpn_host");
814
815    services->AddService("/service/vpn1",
816                         "vpn1_guid",
817                         "vpn1" /* name */,
818                         shill::kTypeVPN,
819                         state,
820                         add_to_visible);
821    services->SetServiceProperty(
822        "/service/vpn1", shill::kProviderProperty, provider_properties_openvpn);
823    profiles->AddService(shared_profile, "/service/vpn1");
824
825    base::DictionaryValue provider_properties_l2tp;
826    provider_properties_l2tp.SetString(shill::kTypeProperty,
827                                       shill::kProviderL2tpIpsec);
828    provider_properties_l2tp.SetString(shill::kHostProperty, "vpn_host2");
829
830    services->AddService("/service/vpn2",
831                         "vpn2_guid",
832                         "vpn2" /* name */,
833                         shill::kTypeVPN,
834                         shill::kStateIdle,
835                         add_to_visible);
836    services->SetServiceProperty(
837        "/service/vpn2", shill::kProviderProperty, provider_properties_l2tp);
838  }
839
840  // Additional device states
841  for (DevicePropertyMap::iterator iter1 = shill_device_property_map_.begin();
842       iter1 != shill_device_property_map_.end(); ++iter1) {
843    std::string device_type = iter1->first;
844    std::string device_path = devices->GetDevicePathForType(device_type);
845    for (ShillPropertyMap::iterator iter2 = iter1->second.begin();
846         iter2 != iter1->second.end(); ++iter2) {
847      devices->SetDeviceProperty(device_path, iter2->first, *(iter2->second));
848      delete iter2->second;
849    }
850  }
851
852  SortManagerServices(true);
853}
854
855// Private methods
856
857void FakeShillManagerClient::PassStubProperties(
858    const DictionaryValueCallback& callback) const {
859  scoped_ptr<base::DictionaryValue> stub_properties(
860      stub_properties_.DeepCopy());
861  stub_properties->SetWithoutPathExpansion(
862      shill::kServiceCompleteListProperty,
863      GetEnabledServiceList(shill::kServiceCompleteListProperty));
864  callback.Run(DBUS_METHOD_CALL_SUCCESS, *stub_properties);
865}
866
867void FakeShillManagerClient::PassStubGeoNetworks(
868    const DictionaryValueCallback& callback) const {
869  callback.Run(DBUS_METHOD_CALL_SUCCESS, stub_geo_networks_);
870}
871
872void FakeShillManagerClient::CallNotifyObserversPropertyChanged(
873    const std::string& property) {
874  // Avoid unnecessary delayed task if we have no observers (e.g. during
875  // initial setup).
876  if (!observer_list_.might_have_observers())
877    return;
878  base::MessageLoop::current()->PostTask(
879      FROM_HERE,
880      base::Bind(&FakeShillManagerClient::NotifyObserversPropertyChanged,
881                 weak_ptr_factory_.GetWeakPtr(),
882                 property));
883}
884
885void FakeShillManagerClient::NotifyObserversPropertyChanged(
886    const std::string& property) {
887  DVLOG(1) << "NotifyObserversPropertyChanged: " << property;
888  base::Value* value = NULL;
889  if (!stub_properties_.GetWithoutPathExpansion(property, &value)) {
890    LOG(ERROR) << "Notify for unknown property: " << property;
891    return;
892  }
893  if (property == shill::kServiceCompleteListProperty) {
894    scoped_ptr<base::ListValue> services(GetEnabledServiceList(property));
895    FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
896                      observer_list_,
897                      OnPropertyChanged(property, *(services.get())));
898    return;
899  }
900  FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
901                    observer_list_,
902                    OnPropertyChanged(property, *value));
903}
904
905base::ListValue* FakeShillManagerClient::GetListProperty(
906    const std::string& property) {
907  base::ListValue* list_property = NULL;
908  if (!stub_properties_.GetListWithoutPathExpansion(
909      property, &list_property)) {
910    list_property = new base::ListValue;
911    stub_properties_.SetWithoutPathExpansion(property, list_property);
912  }
913  return list_property;
914}
915
916bool FakeShillManagerClient::TechnologyEnabled(const std::string& type) const {
917  if (type == shill::kTypeVPN)
918    return true;  // VPN is always "enabled" since there is no associated device
919  if (type == shill::kTypeEthernetEap)
920    return true;
921  bool enabled = false;
922  const base::ListValue* technologies;
923  if (stub_properties_.GetListWithoutPathExpansion(
924          shill::kEnabledTechnologiesProperty, &technologies)) {
925    base::StringValue type_value(type);
926    if (technologies->Find(type_value) != technologies->end())
927      enabled = true;
928  }
929  return enabled;
930}
931
932void FakeShillManagerClient::SetTechnologyEnabled(
933    const std::string& type,
934    const base::Closure& callback,
935    bool enabled) {
936  base::ListValue* enabled_list =
937      GetListProperty(shill::kEnabledTechnologiesProperty);
938  if (enabled)
939    enabled_list->AppendIfNotPresent(new base::StringValue(type));
940  else
941    enabled_list->Remove(base::StringValue(type), NULL);
942  CallNotifyObserversPropertyChanged(
943      shill::kEnabledTechnologiesProperty);
944  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
945  // May affect available services.
946  SortManagerServices(true);
947}
948
949base::ListValue* FakeShillManagerClient::GetEnabledServiceList(
950    const std::string& property) const {
951  base::ListValue* new_service_list = new base::ListValue;
952  const base::ListValue* service_list;
953  if (stub_properties_.GetListWithoutPathExpansion(property, &service_list)) {
954    ShillServiceClient::TestInterface* service_client =
955        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
956    for (base::ListValue::const_iterator iter = service_list->begin();
957         iter != service_list->end(); ++iter) {
958      std::string service_path;
959      if (!(*iter)->GetAsString(&service_path))
960        continue;
961      const base::DictionaryValue* properties =
962          service_client->GetServiceProperties(service_path);
963      if (!properties) {
964        LOG(ERROR) << "Properties not found for service: " << service_path;
965        continue;
966      }
967      std::string type;
968      properties->GetString(shill::kTypeProperty, &type);
969      if (TechnologyEnabled(type))
970        new_service_list->Append((*iter)->DeepCopy());
971    }
972  }
973  return new_service_list;
974}
975
976void FakeShillManagerClient::ScanCompleted(const std::string& device_path,
977                                           const base::Closure& callback) {
978  if (!device_path.empty()) {
979    DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()->
980        SetDeviceProperty(device_path,
981                          shill::kScanningProperty,
982                          base::FundamentalValue(false));
983  }
984  DVLOG(2) << "ScanCompleted";
985  CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
986  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
987}
988
989void FakeShillManagerClient::ParseCommandLineSwitch() {
990  // Default setup
991  SetInitialNetworkState(shill::kTypeEthernet, shill::kStateOnline);
992  SetInitialNetworkState(shill::kTypeWifi, shill::kStateOnline);
993  SetInitialNetworkState(shill::kTypeCellular, shill::kStateIdle);
994  SetInitialNetworkState(shill::kTypeVPN, shill::kStateIdle);
995
996  // Parse additional options
997  CommandLine* command_line = CommandLine::ForCurrentProcess();
998  if (!command_line->HasSwitch(switches::kShillStub))
999    return;
1000
1001  std::string option_str =
1002      command_line->GetSwitchValueASCII(switches::kShillStub);
1003  VLOG(1) << "Parsing command line:" << option_str;
1004  base::StringPairs string_pairs;
1005  base::SplitStringIntoKeyValuePairs(option_str, '=', ',', &string_pairs);
1006  for (base::StringPairs::iterator iter = string_pairs.begin();
1007       iter != string_pairs.end(); ++iter) {
1008    ParseOption((*iter).first, (*iter).second);
1009  }
1010}
1011
1012bool FakeShillManagerClient::ParseOption(const std::string& arg0,
1013                                         const std::string& arg1) {
1014  VLOG(1) << "Parsing command line option: '" << arg0 << "=" << arg1 << "'";
1015  if ((arg0 == "clear" || arg0 == "reset") && arg1 == "1") {
1016    shill_initial_state_map_.clear();
1017    return true;
1018  } else if (arg0 == "interactive") {
1019    int seconds = 3;
1020    if (!arg1.empty())
1021      base::StringToInt(arg1, &seconds);
1022    interactive_delay_ = seconds;
1023    return true;
1024  } else if (arg0 == "sim_lock") {
1025    bool locked = (arg1 == "1") ? true : false;
1026    base::DictionaryValue* simlock_dict = new base::DictionaryValue;
1027    simlock_dict->Set(shill::kSIMLockEnabledProperty,
1028                      new base::FundamentalValue(locked));
1029  // TODO(stevenjb): Investigate why non-empty value breaks UI.
1030  std::string lock_type = "";  // shill::kSIMLockPin
1031    simlock_dict->SetString(shill::kSIMLockTypeProperty, lock_type);
1032    simlock_dict->SetInteger(shill::kSIMLockRetriesLeftProperty, 5);
1033
1034    shill_device_property_map_
1035        [shill::kTypeCellular][shill::kSIMLockStatusProperty] = simlock_dict;
1036    shill_device_property_map_
1037        [shill::kTypeCellular][shill::kTechnologyFamilyProperty] =
1038            new base::StringValue(shill::kNetworkTechnologyGsm);
1039    return true;
1040  }
1041  return SetInitialNetworkState(arg0, arg1);
1042}
1043
1044bool FakeShillManagerClient::SetInitialNetworkState(std::string type_arg,
1045                                                    std::string state_arg) {
1046  std::string state;
1047  state_arg = base::StringToLowerASCII(state_arg);
1048  if (state_arg.empty() || state_arg == "1" || state_arg == "on" ||
1049      state_arg == "enabled" || state_arg == "connected" ||
1050      state_arg == "online") {
1051    // Enabled and connected (default value)
1052    state = shill::kStateOnline;
1053  } else if (state_arg == "0" || state_arg == "off" ||
1054             state_arg == "inactive" || state_arg == shill::kStateIdle) {
1055    // Technology enabled, services are created but are not connected.
1056    state = shill::kStateIdle;
1057  } else if (state_arg == "disabled" || state_arg == "disconnect") {
1058    // Technology disabled but available, services created but not connected.
1059    state = kNetworkDisabled;
1060  } else if (state_arg == "none" || state_arg == "offline") {
1061    // Technology not available, do not create services.
1062    state = kTechnologyUnavailable;
1063  } else if (state_arg == "portal") {
1064    // Technology is enabled, a service is connected and in Portal state.
1065    state = shill::kStatePortal;
1066  } else if (state_arg == "active" || state_arg == "activated") {
1067    // Technology is enabled, a service is connected and Activated.
1068    state = kNetworkActivated;
1069  } else {
1070    LOG(ERROR) << "Unrecognized initial state: " << state_arg;
1071    return false;
1072  }
1073
1074  type_arg = base::StringToLowerASCII(type_arg);
1075  // Special cases
1076  if (type_arg == "wireless") {
1077    shill_initial_state_map_[shill::kTypeWifi] = state;
1078    shill_initial_state_map_[shill::kTypeCellular] = state;
1079    return true;
1080  }
1081  // Convenience synonyms.
1082  if (type_arg == "eth")
1083    type_arg = shill::kTypeEthernet;
1084
1085  if (type_arg != shill::kTypeEthernet &&
1086      type_arg != shill::kTypeWifi &&
1087      type_arg != shill::kTypeCellular &&
1088      type_arg != shill::kTypeWimax &&
1089      type_arg != shill::kTypeVPN) {
1090    LOG(WARNING) << "Unrecognized Shill network type: " << type_arg;
1091    return false;
1092  }
1093
1094  // Unconnected or disabled ethernet is the same as unavailable.
1095  if (type_arg == shill::kTypeEthernet &&
1096      (state == shill::kStateIdle || state == kNetworkDisabled)) {
1097    state = kTechnologyUnavailable;
1098  }
1099
1100  shill_initial_state_map_[type_arg] = state;
1101  return true;
1102}
1103
1104std::string FakeShillManagerClient::GetInitialStateForType(
1105    const std::string& type,
1106    bool* enabled) {
1107  std::map<std::string, std::string>::const_iterator iter =
1108      shill_initial_state_map_.find(type);
1109  if (iter == shill_initial_state_map_.end()) {
1110    *enabled = false;
1111    return kTechnologyUnavailable;
1112  }
1113  std::string state = iter->second;
1114  if (state == kNetworkDisabled) {
1115    *enabled = false;
1116    return shill::kStateIdle;
1117  }
1118  *enabled = true;
1119  if ((state == shill::kStatePortal && type != shill::kTypeWifi) ||
1120      (state == kNetworkActivated && type != shill::kTypeCellular)) {
1121    LOG(WARNING) << "Invalid state: " << state << " for " << type;
1122    return shill::kStateIdle;
1123  }
1124  return state;
1125}
1126
1127}  // namespace chromeos
1128