| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SHILL_DBUS_CLIENT_CLIENT_H_ |
| #define SHILL_DBUS_CLIENT_CLIENT_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind_helpers.h> |
| #include <base/callback_forward.h> |
| #include <base/macros.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/memory/weak_ptr.h> |
| #include <brillo/brillo_export.h> |
| #include <shill/dbus-proxies.h> |
| #include <chromeos/dbus/service_constants.h> |
| |
| namespace shill { |
| |
| // Shill D-Bus client for listening to common manager, service and device |
| // properties. This class is the result of an effort to consolidate a lot of |
| // duplicated boilerplate across multiple platform2 packages. |
| // TODO(garrick): Integrate into applicable platform2 packages. |
| class BRILLO_EXPORT Client { |
| public: |
| // IPConfig for a device. If the device does not have a valid ipv4/ipv6 |
| // config, the corresponding fields will be empty or 0. |
| // TODO(jiejiang): add the following fields into this struct: |
| // - IPv4 search domains |
| // - IPv6 search domains |
| // - MTU (one only per network) |
| struct IPConfig { |
| bool operator==(const IPConfig& that) { |
| return this->ipv4_prefix_length == that.ipv4_prefix_length && |
| this->ipv4_address == that.ipv4_address && |
| this->ipv4_gateway == that.ipv4_gateway && |
| this->ipv4_dns_addresses == that.ipv4_dns_addresses && |
| this->ipv6_prefix_length == that.ipv6_prefix_length && |
| this->ipv6_address == that.ipv6_address && |
| this->ipv6_gateway == that.ipv6_gateway && |
| this->ipv6_dns_addresses == that.ipv6_dns_addresses; |
| } |
| |
| int ipv4_prefix_length; |
| std::string ipv4_address; |
| std::string ipv4_gateway; |
| std::vector<std::string> ipv4_dns_addresses; |
| |
| int ipv6_prefix_length; |
| // Note due to the limitation of shill, we will only get one IPv6 address |
| // from it. This address should be the privacy address for device with type |
| // of ethernet or wifi. |
| // TODO(garrick): Support multiple IPv6 configurations. |
| std::string ipv6_address; |
| std::string ipv6_gateway; |
| std::vector<std::string> ipv6_dns_addresses; |
| }; |
| |
| // Represents a subset of properties from org.chromium.flimflam.Device. |
| // TODO(jiejiang): add the following fields into this struct: |
| // - the DBus path of the Service associated to this Device if any |
| // - the connection state of the Service, if possible by translating back to |
| // the enum shill::Service::ConnectState |
| struct Device { |
| // A subset of shill::Technology::Type. |
| enum class Type { |
| kUnknown, |
| kCellular, |
| kEthernet, |
| kEthernetEap, |
| kGuestInterface, |
| kLoopback, |
| kPPP, |
| kPPPoE, |
| kTunnel, |
| kVPN, |
| kWifi, |
| }; |
| |
| // From shill::ConnectState. |
| enum class ConnectionState { |
| kUnknown, |
| kIdle, |
| kCarrier, |
| kAssociation, |
| kConfiguration, |
| kReady, |
| kNoConnectivity, |
| kRedirectFound, |
| kPortalSuspected, |
| kOnline, |
| kOffline, |
| kFailure, |
| kDisconnect, |
| kActivationFailure, |
| }; |
| |
| bool operator==(const Device& that) { |
| return this->type == that.type && this->ifname == that.ifname && |
| this->ipconfig == that.ipconfig; |
| } |
| |
| Type type; |
| ConnectionState state; |
| std::string ifname; |
| IPConfig ipconfig; |
| }; |
| |
| using DefaultServiceChangedHandler = |
| base::Callback<void(const std::string& type)>; |
| using DeviceChangedHandler = base::Callback<void(const Device* const)>; |
| |
| explicit Client(scoped_refptr<dbus::Bus> bus); |
| virtual ~Client() = default; |
| Client(const Client&) = delete; |
| Client& operator=(const Client&) = delete; |
| |
| // Initiates the connection to DBus and starts processing signals. |
| void Init(); |
| |
| // |handler| will be invoked whenever the default service changes, i.e. |
| // whenever the default service switches from "none" to a valid path or |
| // vice-versa. |
| // Multiple handlers may be registered. |
| void RegisterDefaultServiceChangedHandler( |
| const DefaultServiceChangedHandler& handler); |
| |
| // |handler| will be invoked whenever the device associated with the default |
| // service changes. The following changes will triggers this handler: |
| // * The default service itself changes, |
| // * The default service is connected or disconnected, |
| // * The device connected to the default service changes, |
| // * The IP configuration of the default device changes. |
| // |
| // If the default service is disconnected, the device will be null. |
| // Multiple handlers may be registered. |
| void RegisterDefaultDeviceChangedHandler(const DeviceChangedHandler& handler); |
| |
| // |handler| will be invoked whenever there is a change to tracked properties |
| // which currently include: |
| // * The device's IPConfigs, |
| // * The state of the device's connected service. |
| // Multiple handlers may be registered. |
| void RegisterDeviceChangedHandler(const DeviceChangedHandler& handler); |
| |
| // |handler| will be invoked whenever a device is added or removed from shill. |
| // Note that if the default service switches to VPN, the corresponding device |
| // will be added and tracked. This will not occur for any other type of |
| // virtual device. Handlers can use |Device.type| to filter, if necessary. |
| // Multiple handlers may be registered. |
| void RegisterDeviceAddedHandler(const DeviceChangedHandler& handler); |
| void RegisterDeviceRemovedHandler(const DeviceChangedHandler& handler); |
| |
| protected: |
| // All of the methods and members with protected access scope are needed for |
| // unit testing. |
| |
| // Invoked when the DBus service owner name changes, which occurs when the |
| // service is stopped (new_owner is empty) or restarted (new_owner != |
| // old_owner) |
| // This will trigger any existing proxies to the existing service to be reset, |
| // and a new manager proxy will be established. |
| void OnOwnerChange(const std::string& old_owner, |
| const std::string& new_owner); |
| |
| // This callback is invoked whenever a manager property change signal is |
| // received; if the property is one we pay attention to the corresponding |
| // Handle*Changed handler will be called. |
| void OnManagerPropertyChange(const std::string& property_name, |
| const brillo::Any& property_value); |
| |
| // This callback is invoked whenever the default service property change |
| // signal is received; if the property is one we pay attention to the |
| // corresponding Handler*Changed handler will be called. |
| void OnDefaultServicePropertyChange(const std::string& property_name, |
| const brillo::Any& property_value); |
| |
| // This callback is invoked whenever a device property change signal is |
| // received; if the property is one we pay attention to the corresponding |
| // handler will be invoked. If the device is new, it will be added to the |
| // internal list that are tracked. |
| void OnDevicePropertyChange(bool device_added, |
| const std::string& device_path, |
| const std::string& property_name, |
| const brillo::Any& property_value); |
| |
| // This callback is invoked whenever a service property change signal is |
| // received for a service that is connected to a particular device. In this |
| // case |device_path| will be non-empty. Note that if the service in question |
| // is also the default service, this handler will be called as well as the |
| // default service change handler. |
| void OnServicePropertyChange(const std::string& device_path, |
| const std::string& property_name, |
| const brillo::Any& property_value); |
| |
| // Methods for managing proxy objects. These are overridden in tests to ensure |
| // registration hooks, callbacks and properties can be plumbed back through |
| // the interfaces as needed. |
| virtual void NewManagerProxy(); |
| virtual void ReleaseManagerProxy(); |
| virtual void NewDefaultServiceProxy(const dbus::ObjectPath& service_path); |
| virtual void ReleaseDefaultServiceProxy(); |
| virtual std::unique_ptr<org::chromium::flimflam::DeviceProxyInterface> |
| NewDeviceProxy(const dbus::ObjectPath& device_path); |
| virtual std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface> |
| NewServiceProxy(const dbus::ObjectPath& service_path); |
| |
| std::unique_ptr<org::chromium::flimflam::ManagerProxyInterface> |
| manager_proxy_; |
| std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface> |
| default_service_proxy_; |
| |
| private: |
| // This callback is invoked whenever the default service changes, that is, |
| // when it switches from one service to another. If applicable, the callback |
| // set via RegisterDefaultServiceChangedHandler will be invoked. |
| void HandleDefaultServiceChanged(const brillo::Any& property_value); |
| |
| // This callback is invoked whenever the (physical) device list provided by |
| // shill changes. |
| void HandleDevicesChanged(const brillo::Any& property_value); |
| |
| // Invoked whenever a device's selected service changes. |
| Device* HandleSelectedServiceChanged(const std::string& device_path, |
| const brillo::Any& property_value); |
| |
| // This callback is invoked whenever a new manager proxy is created. It will |
| // trigger the discovery of the default service. |
| void OnManagerPropertyChangeRegistration(const std::string& interface, |
| const std::string& signal_name, |
| bool success); |
| |
| // This callback is invoked whenever a new default service proxy is created. |
| // It will trigger the discovery of the device associated with the default |
| // service. |
| void OnDefaultServicePropertyChangeRegistration( |
| const std::string& interface, |
| const std::string& signal_name, |
| bool success); |
| |
| // This callback is invoked whenever a new device proxy is created. It will |
| // trigger the discovery of the device properties we care about including its |
| // type, interface name and IP configuration. |
| void OnDevicePropertyChangeRegistration(const std::string& device_path, |
| const std::string& interface, |
| const std::string& signal_name, |
| bool success); |
| |
| // This callback is invoked whenever a new selected service proxy is created. |
| // It will trigger the discovery of service properties we care about including |
| // the connected state. |
| void OnServicePropertyChangeRegistration(const std::string& device_path, |
| const std::string& interface, |
| const std::string& signal_name, |
| bool success); |
| |
| void SetupManagerProxy(); |
| void SetupDefaultServiceProxy(const dbus::ObjectPath& service_path); |
| void SetupSelectedServiceProxy(const dbus::ObjectPath& service_path, |
| const dbus::ObjectPath& device_path); |
| void SetupDeviceProxy(const dbus::ObjectPath& device_path); |
| |
| // Wraps a device with its DBus proxy on which property change signals are |
| // received. |
| class DeviceWrapper { |
| public: |
| DeviceWrapper( |
| scoped_refptr<dbus::Bus> bus, |
| std::unique_ptr<org::chromium::flimflam::DeviceProxyInterface> proxy) |
| : bus_(bus), proxy_(std::move(proxy)) {} |
| ~DeviceWrapper() { |
| bus_->RemoveObjectProxy(kFlimflamServiceName, proxy_->GetObjectPath(), |
| base::DoNothing()); |
| if (svc_proxy_) |
| bus_->RemoveObjectProxy(kFlimflamServiceName, |
| svc_proxy_->GetObjectPath(), base::DoNothing()); |
| } |
| DeviceWrapper(const DeviceWrapper&) = delete; |
| DeviceWrapper& operator=(const DeviceWrapper&) = delete; |
| |
| Device* device() { return &device_; } |
| org::chromium::flimflam::DeviceProxyInterface* proxy() { |
| return proxy_.get(); |
| } |
| void set_service_proxy( |
| std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface> proxy) { |
| // Note - expect this to be called once - if that ever changes, call |
| // RemoveObjectProxy first. |
| svc_proxy_ = std::move(proxy); |
| } |
| org::chromium::flimflam::ServiceProxyInterface* service_proxy() { |
| return svc_proxy_.get(); |
| } |
| |
| private: |
| scoped_refptr<dbus::Bus> bus_; |
| Device device_; |
| std::unique_ptr<org::chromium::flimflam::DeviceProxyInterface> proxy_; |
| std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface> svc_proxy_; |
| }; |
| |
| void AddDevice(const dbus::ObjectPath& path); |
| |
| // Reads the list of IPConfigs for a device and composes them into an IPConfig |
| // data structure. |
| IPConfig ParseIPConfigsProperty(const std::string& device_path, |
| const brillo::Any& property_value); |
| |
| scoped_refptr<dbus::Bus> bus_; |
| |
| std::vector<DefaultServiceChangedHandler> default_service_handlers_; |
| std::vector<DeviceChangedHandler> default_device_handlers_; |
| std::vector<DeviceChangedHandler> device_handlers_; |
| std::vector<DeviceChangedHandler> device_added_handlers_; |
| std::vector<DeviceChangedHandler> device_removed_handlers_; |
| |
| bool default_service_connected_ = false; |
| std::string default_device_path_; |
| |
| // Tracked devices keyed by path. |
| std::map<std::string, std::unique_ptr<DeviceWrapper>> devices_; |
| |
| base::WeakPtrFactory<Client> weak_factory_{this}; |
| }; |
| |
| } // namespace shill |
| |
| #endif // SHILL_DBUS_CLIENT_CLIENT_H_ |