1# Copyright (c) 2013 The Chromium OS 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
5import copy
6import logging
7import pprint
8import sys
9
10from autotest_lib.client.common_lib.cros import xmlrpc_types
11from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
12
13
14def deserialize(serialized):
15    """Deserialize an argument to the XmlRpc proxy.
16
17    @param serialized dict representing a serialized object.
18    @return the corresponding deserialized object.
19
20    """
21    return xmlrpc_types.deserialize(serialized, module=sys.modules[__name__])
22
23
24class AssociationParameters(xmlrpc_types.XmlRpcStruct):
25    """Describes parameters used in WiFi connection attempts."""
26
27    DEFAULT_DISCOVERY_TIMEOUT = 15
28    DEFAULT_ASSOCIATION_TIMEOUT = 15
29    DEFAULT_CONFIGURATION_TIMEOUT = 15
30    # Mode for most routers and access points.
31    STATION_TYPE_MANAGED = 'managed'
32    # Mode for certain kinds of p2p networks like old Android phone hotspots.
33    STATION_TYPE_IBSS = 'ibss'
34
35    @property
36    def security(self):
37        """@return string security type for this network."""
38        return self.security_config.security
39
40
41    @property
42    def security_parameters(self):
43        """@return dict of service property/value pairs related to security."""
44        return self.security_config.get_shill_service_properties()
45
46
47    def __init__(self, ssid=None, security_config=None,
48                 discovery_timeout=DEFAULT_DISCOVERY_TIMEOUT,
49                 association_timeout=DEFAULT_ASSOCIATION_TIMEOUT,
50                 configuration_timeout=DEFAULT_CONFIGURATION_TIMEOUT,
51                 is_hidden=False, save_credentials=False, station_type=None,
52                 expect_failure=False, guid=None, autoconnect=None,
53                 bgscan_config=None):
54        """Construct an AssociationParameters.
55
56        @param ssid string the network to connect to (e.g. 'GoogleGuest').
57        @param security_config SecurityConfig object or serialized version.
58        @param discovery_timeout int timeout for discovery in seconds.
59        @param association_timeout int timeout for association in seconds.
60        @param configuration_timeout int timeout for configuration in seconds.
61        @param is_hidden bool True iff this is a hidden service.
62        @param save_credentials True iff the credentials should be saved for
63                this service.
64        @param station_type string station type to connect with.  Usually
65                left unfilled unless we're attempting to connect to a
66                non-managed BSS.  One of STATION_TYPE_* above.
67        @param expect_failure bool True if we expect this connection attempt to
68                fail.
69        @param guid string unique identifier of this service.
70        @param autoconnect: bool or None.  None indicates that this should not
71                be set one way or the other, while a boolean indicates a desired
72                value.
73
74        """
75        super(AssociationParameters, self).__init__()
76        self.ssid = ssid
77        # The security config is a little tricky.  When we're being deserialized
78        # this is passed to us in the form of a dictionary which also needs
79        # to be deserialized into a real object.
80        if isinstance(security_config, dict):
81            self.security_config = xmlrpc_security_types.deserialize(
82                    security_config)
83        elif security_config is not None:
84            self.security_config = copy.copy(security_config)
85        else:
86            self.security_config = xmlrpc_security_types.SecurityConfig()
87
88        # The bgscan configuration is similar to the security configuration.
89        if isinstance(bgscan_config, dict):
90            self.bgscan_config = deserialize(bgscan_config)
91        elif bgscan_config is not None:
92            self.bgscan_config = copy.copy(bgscan_config)
93        else:
94            self.bgscan_config = BgscanConfiguration()
95        self.discovery_timeout = discovery_timeout
96        self.association_timeout = association_timeout
97        self.configuration_timeout = configuration_timeout
98        self.is_hidden = is_hidden
99        self.save_credentials = save_credentials
100        self.station_type = station_type
101        self.expect_failure = expect_failure
102        self.guid = guid
103        self.autoconnect = autoconnect
104
105
106    def __str__(self):
107        """Returns a formatted string of member parameters"""
108        return pprint.pformat(self.__dict__)
109
110
111class AssociationResult(xmlrpc_types.XmlRpcStruct):
112    """Describes the result of an association attempt."""
113
114    def __init__(self, success=False, discovery_time=-1.0,
115                 association_time=-1.0, configuration_time=-1.0,
116                 failure_reason='unknown'):
117        """Construct an AssociationResult.
118
119        @param success bool True iff we were successful in connecting to
120                this WiFi network.
121        @param discovery_time int number of seconds it took to find and call
122                connect on a network from the time the proxy is told to connect.
123                This includes scanning time.
124        @param association_time int number of seconds it takes from the moment
125                that we call connect to the moment we're fully associated with
126                the BSS.  This includes wpa handshakes.
127        @param configuration_time int number of seconds it takes from
128                association till we have an IP address and mark the network as
129                being either online or portalled.
130        @param failure_reason int holds a descriptive reason for why the
131                negotiation failed when |successs| is False.  Undefined
132                otherwise.
133
134        """
135        super(AssociationResult, self).__init__()
136        self.success = success
137        self.discovery_time = discovery_time
138        self.association_time = association_time
139        self.configuration_time = configuration_time
140        self.failure_reason = failure_reason
141
142
143    @staticmethod
144    def from_dbus_proxy_output(raw):
145        """Factory for AssociationResult.
146
147        The object which knows how to talk over DBus to shill is not part of
148        autotest and as a result can't return a AssociationResult.  Instead,
149        it returns a similar looing tuple, which we'll parse.
150
151        @param raw tuple from ShillProxy.
152        @return AssociationResult parsed output from ShillProxy.
153
154        """
155        return AssociationResult(success=raw[0],
156                                 discovery_time=raw[1],
157                                 association_time=raw[2],
158                                 configuration_time=raw[3],
159                                 failure_reason=raw[4])
160
161
162class BgscanConfiguration(xmlrpc_types.XmlRpcStruct):
163    """Describes how to configure wpa_supplicant on a DUT."""
164
165    # Clears shill's bgscan method property on the WiFi device.
166    # This causes shill to choose between simple and no bgscan
167    # depending on the number of visible BSS's for a network.
168    SCAN_METHOD_DEFAULT = 'default'
169    # Disables background scan entirely.
170    SCAN_METHOD_NONE = 'none'
171    # A periodic background scan based on signal strength.
172    SCAN_METHOD_SIMPLE = 'simple'
173
174    # These three parameters come out shill's wifi.cc.
175    # and refer to inputs to the simple scanning method.
176    DEFAULT_SHORT_INTERVAL_SECONDS = 30
177    DEFAULT_LONG_INTERVAL_SECONDS = 180
178    DEFAULT_SIGNAL_THRESHOLD = -50
179
180    def __init__(self, interface=None, signal=DEFAULT_SIGNAL_THRESHOLD,
181                 short_interval=DEFAULT_SHORT_INTERVAL_SECONDS,
182                 long_interval=DEFAULT_LONG_INTERVAL_SECONDS,
183                 method=SCAN_METHOD_DEFAULT):
184        """Construct a BgscanConfiguration.
185
186        @param interface string interface to configure (e.g. wlan0).
187        @param signal int signal threshold to scan below.
188        @param short_interval int wpa_supplicant short scanning interval.
189        @param long_interval int wpa_supplicant normal scanning interval.
190        @param method string a valid wpa_supplicant scanning algorithm (e.g.
191                any of SCAN_METHOD_* above).
192
193        """
194        super(BgscanConfiguration, self).__init__()
195        self.interface = interface
196        self.signal = signal
197        self.short_interval = short_interval
198        self.long_interval = long_interval
199        self.method = method
200
201
202    def set_auto_signal(self, signal_average, signal_offset=None,
203                        signal_noise=None):
204        """Set the signal threshold automatically from observed parameters.
205
206        @param signal_average int average signal level.
207        @param signal_offset int amount to adjust the average by.
208        @param signal_noise int amount of background noise observed.
209
210        """
211        signal = signal_average
212        if signal_offset:
213            signal += signal_offset
214        if signal_noise:
215            # Compensate for real noise vs standard estimate
216            signal -= 95 + signal_noise
217        logging.debug('Setting signal via auto configuration: '
218                      'avg=%d, offset=%r, noise=%r => signal=%d.',
219                      signal_average, signal_offset, signal_noise, signal)
220        self.signal = signal
221
222
223class ConfigureServiceParameters(xmlrpc_types.XmlRpcStruct):
224    """Describes a group of optional settings for use with ConfigureService.
225
226    The Manager in shill has a method ConfigureService which takes a dictionary
227    of parameters, and uses some of them to look up a service, and sets the
228    remainder of the properties on the service.  This struct represents
229    some of the optional parameters that can be set in this way.  Current
230    consumers of this interface look up the service by GUID.
231
232    """
233
234    def __init__(self, guid, passphrase=None, autoconnect=None):
235        """Construct a ConfigureServiceParameters.
236
237        @param guid string GUID of the service to configure.
238        @param passphrase string optional psk passphrase.
239        @param autoconnect: bool or None.  None indicates that this should not
240                be set one way or the other, while a boolean indicates a desired
241                value.
242
243        """
244        super(ConfigureServiceParameters, self).__init__()
245        self.guid = guid
246        self.passphrase = passphrase
247        self.autoconnect = autoconnect
248