text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chromeos/dbus/shill/shill_device_client.h"
  
  #include <map>
  #include <utility>
  
  #include "base/bind.h"
  #include "base/macros.h"
  #include "base/stl_util.h"
  #include "base/values.h"
  #include "chromeos/dbus/shill/fake_shill_device_client.h"
  #include "chromeos/dbus/shill/shill_property_changed_observer.h"
  #include "dbus/bus.h"
  #include "dbus/message.h"
  #include "dbus/object_path.h"
  #include "dbus/object_proxy.h"
  #include "dbus/values_util.h"
  #include "net/base/ip_endpoint.h"
  #include "third_party/cros_system_api/dbus/service_constants.h"
  
  namespace chromeos {
  
  namespace {
  
  ShillDeviceClient* g_instance = nullptr;
  
  // The ShillDeviceClient implementation.
  class ShillDeviceClientImpl : public ShillDeviceClient {
   public:
    explicit ShillDeviceClientImpl(dbus::Bus* bus) : bus_(bus) {}
  
    ~ShillDeviceClientImpl() override {
      for (HelperMap::iterator iter = helpers_.begin(); iter != helpers_.end();
           ++iter) {
        // This *should* never happen, yet we're getting crash reports that
        // seem to imply that it does happen sometimes.  Adding CHECKs here
        // so we can determine more accurately where the problem lies.
        // See: http://crbug.com/170541
        CHECK(iter->second) << "null Helper found in helper list.";
        delete iter->second;
      }
      helpers_.clear();
    }
  
    ///////////////////////////////////////
    // ShillDeviceClient overrides.
    void AddPropertyChangedObserver(
        const dbus::ObjectPath& device_path,
        ShillPropertyChangedObserver* observer) override {
      GetHelper(device_path)->AddPropertyChangedObserver(observer);
    }
  
    void RemovePropertyChangedObserver(
        const dbus::ObjectPath& device_path,
        ShillPropertyChangedObserver* observer) override {
      GetHelper(device_path)->RemovePropertyChangedObserver(observer);
    }
  
    void GetProperties(const dbus::ObjectPath& device_path,
                       DictionaryValueCallback callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kGetPropertiesFunction);
      GetHelper(device_path)
          ->CallDictionaryValueMethod(&method_call, std::move(callback));
    }
  
    void SetProperty(const dbus::ObjectPath& device_path,
                     const std::string& name,
                     const base::Value& value,
                     base::OnceClosure callback,
                     ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kSetPropertyFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(name);
      ShillClientHelper::AppendValueDataAsVariant(&writer, value);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void ClearProperty(const dbus::ObjectPath& device_path,
                       const std::string& name,
                       VoidDBusMethodCallback callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kClearPropertyFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(name);
      GetHelper(device_path)->CallVoidMethod(&method_call, std::move(callback));
    }
  
    void RequirePin(const dbus::ObjectPath& device_path,
                    const std::string& pin,
                    bool require,
                    base::OnceClosure callback,
                    ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kRequirePinFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(pin);
      writer.AppendBool(require);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void EnterPin(const dbus::ObjectPath& device_path,
                  const std::string& pin,
                  base::OnceClosure callback,
                  ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kEnterPinFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(pin);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void UnblockPin(const dbus::ObjectPath& device_path,
                    const std::string& puk,
                    const std::string& pin,
                    base::OnceClosure callback,
                    ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kUnblockPinFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(puk);
      writer.AppendString(pin);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void ChangePin(const dbus::ObjectPath& device_path,
                   const std::string& old_pin,
                   const std::string& new_pin,
                   base::OnceClosure callback,
                   ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kChangePinFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(old_pin);
      writer.AppendString(new_pin);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void Register(const dbus::ObjectPath& device_path,
                  const std::string& network_id,
                  base::OnceClosure callback,
                  ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kRegisterFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(network_id);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void Reset(const dbus::ObjectPath& device_path,
               base::OnceClosure callback,
               ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kResetFunction);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void PerformTDLSOperation(const dbus::ObjectPath& device_path,
                              const std::string& operation,
                              const std::string& peer,
                              StringCallback callback,
                              ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kPerformTDLSOperationFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(operation);
      writer.AppendString(peer);
      GetHelper(device_path)
          ->CallStringMethodWithErrorCallback(&method_call, std::move(callback),
                                              std::move(error_callback));
    }
  
    void AddWakeOnPacketConnection(const dbus::ObjectPath& device_path,
                                   const net::IPEndPoint& ip_endpoint,
                                   base::OnceClosure callback,
                                   ErrorCallback error_callback) override {
      if (ip_endpoint.address().empty()) {
        LOG(ERROR) << "AddWakeOnPacketConnection: null address";
        return;
      }
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kAddWakeOnPacketConnectionFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(ip_endpoint.ToStringWithoutPort());
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void AddWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                                const std::vector<std::string>& types,
                                base::OnceClosure callback,
                                ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kAddWakeOnPacketOfTypesFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendArrayOfStrings(types);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void RemoveWakeOnPacketConnection(const dbus::ObjectPath& device_path,
                                      const net::IPEndPoint& ip_endpoint,
                                      base::OnceClosure callback,
                                      ErrorCallback error_callback) override {
      if (ip_endpoint.address().empty()) {
        LOG(ERROR) << "RemoveWakeOnPacketConnection: null address";
        return;
      }
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kRemoveWakeOnPacketConnectionFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(ip_endpoint.ToStringWithoutPort());
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void RemoveWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                                   const std::vector<std::string>& types,
                                   base::OnceClosure callback,
                                   ErrorCallback error_callback) override {
      dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                   shill::kRemoveWakeOnPacketOfTypesFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendArrayOfStrings(types);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void RemoveAllWakeOnPacketConnections(const dbus::ObjectPath& device_path,
                                          base::OnceClosure callback,
                                          ErrorCallback error_callback) override {
      dbus::MethodCall method_call(
          shill::kFlimflamDeviceInterface,
          shill::kRemoveAllWakeOnPacketConnectionsFunction);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    void SetUsbEthernetMacAddressSource(const dbus::ObjectPath& device_path,
                                        const std::string& source,
                                        base::OnceClosure callback,
                                        ErrorCallback error_callback) override {
      dbus::MethodCall method_call(
          shill::kFlimflamDeviceInterface,
          shill::kSetUsbEthernetMacAddressSourceFunction);
      dbus::MessageWriter writer(&method_call);
      writer.AppendString(source);
      GetHelper(device_path)
          ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
                                            std::move(error_callback));
    }
  
    TestInterface* GetTestInterface() override { return nullptr; }
  
   private:
    typedef std::map<std::string, ShillClientHelper*> HelperMap;
  
    // Returns the corresponding ShillClientHelper for the profile.
    ShillClientHelper* GetHelper(const dbus::ObjectPath& device_path) {
      HelperMap::iterator it = helpers_.find(device_path.value());
      if (it != helpers_.end()) {
        CHECK(it->second) << "Found a null helper in the list.";
        return it->second;
      }
  
      // There is no helper for the profile, create it.
      dbus::ObjectProxy* object_proxy =
          bus_->GetObjectProxy(shill::kFlimflamServiceName, device_path);
      ShillClientHelper* helper = new ShillClientHelper(object_proxy);
      CHECK(helper) << "Unable to create Shill client helper.";
      helper->MonitorPropertyChanged(shill::kFlimflamDeviceInterface);
      helpers_.insert(HelperMap::value_type(device_path.value(), helper));
      return helper;
    }
  
    dbus::Bus* bus_;
    HelperMap helpers_;
  
    DISALLOW_COPY_AND_ASSIGN(ShillDeviceClientImpl);
  };
  
  }  // namespace
  
  ShillDeviceClient::ShillDeviceClient() {
    DCHECK(!g_instance);
    g_instance = this;
  }
  
  ShillDeviceClient::~ShillDeviceClient() {
    DCHECK_EQ(this, g_instance);
    g_instance = nullptr;
  }
  
  // static
  void ShillDeviceClient::Initialize(dbus::Bus* bus) {
    DCHECK(bus);
    new ShillDeviceClientImpl(bus);
  }
  
  // static
  void ShillDeviceClient::InitializeFake() {
    new FakeShillDeviceClient();
  }
  
  // static
  void ShillDeviceClient::Shutdown() {
    DCHECK(g_instance);
    delete g_instance;
  }
  
  // static
  ShillDeviceClient* ShillDeviceClient::Get() {
    return g_instance;
  }
  
  }  // namespace chromeos
  