• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import dbus
8import logging
9import logging.handlers
10import multiprocessing
11
12import common
13from autotest_lib.client.common_lib import utils
14from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
15from autotest_lib.client.cros import xmlrpc_server
16from autotest_lib.client.cros import constants
17from autotest_lib.client.cros import cros_ui
18from autotest_lib.client.cros import tpm_store
19from autotest_lib.client.cros.networking import shill_proxy
20from autotest_lib.client.cros.networking import wifi_proxy
21from autotest_lib.client.cros.power import sys_power
22
23
24class ShillXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
25    """Exposes methods called remotely during WiFi autotests.
26
27    All instance methods of this object without a preceding '_' are exposed via
28    an XMLRPC server.  This is not a stateless handler object, which means that
29    if you store state inside the delegate, that state will remain around for
30    future calls.
31
32    """
33
34    DEFAULT_TEST_PROFILE_NAME = 'test'
35    DBUS_DEVICE = 'Device'
36
37    def __init__(self):
38        self._wifi_proxy = wifi_proxy.WifiProxy()
39        self._tpm_store = tpm_store.TPMStore()
40
41
42    def __enter__(self):
43        super(ShillXmlRpcDelegate, self).__enter__()
44        if not cros_ui.stop(allow_fail=True):
45            logging.error('UI did not stop, there could be trouble ahead.')
46        self._tpm_store.__enter__()
47
48
49    def __exit__(self, exception, value, traceback):
50        super(ShillXmlRpcDelegate, self).__exit__(exception, value, traceback)
51        self._tpm_store.__exit__(exception, value, traceback)
52        self.enable_ui()
53
54
55    @xmlrpc_server.dbus_safe(False)
56    def create_profile(self, profile_name):
57        """Create a shill profile.
58
59        @param profile_name string name of profile to create.
60        @return True on success, False otherwise.
61
62        """
63        self._wifi_proxy.manager.CreateProfile(profile_name)
64        return True
65
66
67    @xmlrpc_server.dbus_safe(False)
68    def push_profile(self, profile_name):
69        """Push a shill profile.
70
71        @param profile_name string name of profile to push.
72        @return True on success, False otherwise.
73
74        """
75        self._wifi_proxy.manager.PushProfile(profile_name)
76        return True
77
78
79    @xmlrpc_server.dbus_safe(False)
80    def pop_profile(self, profile_name):
81        """Pop a shill profile.
82
83        @param profile_name string name of profile to pop.
84        @return True on success, False otherwise.
85
86        """
87        if profile_name is None:
88            self._wifi_proxy.manager.PopAnyProfile()
89        else:
90            self._wifi_proxy.manager.PopProfile(profile_name)
91        return True
92
93
94    @xmlrpc_server.dbus_safe(False)
95    def remove_profile(self, profile_name):
96        """Remove a profile from disk.
97
98        @param profile_name string name of profile to remove.
99        @return True on success, False otherwise.
100
101        """
102        self._wifi_proxy.manager.RemoveProfile(profile_name)
103        return True
104
105
106    @xmlrpc_server.dbus_safe(False)
107    def clean_profiles(self):
108        """Pop and remove shill profiles above the default profile.
109
110        @return True on success, False otherwise.
111
112        """
113        while True:
114            active_profile = self._wifi_proxy.get_active_profile()
115            profile_name = self._wifi_proxy.dbus2primitive(
116                    active_profile.GetProperties(utf8_strings=True)['Name'])
117            if profile_name == 'default':
118                return True
119            self._wifi_proxy.manager.PopProfile(profile_name)
120            self._wifi_proxy.manager.RemoveProfile(profile_name)
121
122
123    @xmlrpc_server.dbus_safe(False)
124    def configure_service_by_guid(self, raw_params):
125        """Configure a service referenced by a GUID.
126
127        @param raw_params serialized ConfigureServiceParameters.
128
129        """
130        params = xmlrpc_datatypes.deserialize(raw_params)
131        shill = self._wifi_proxy
132        properties = {}
133        if params.autoconnect is not None:
134            properties[shill.SERVICE_PROPERTY_AUTOCONNECT] = params.autoconnect
135        if params.passphrase is not None:
136            properties[shill.SERVICE_PROPERTY_PASSPHRASE] = params.passphrase
137        if properties:
138            self._wifi_proxy.configure_service_by_guid(params.guid, properties)
139        return True
140
141
142    @xmlrpc_server.dbus_safe(False)
143    def configure_wifi_service(self, raw_params):
144        """Configure a WiFi service
145
146        @param raw_params serialized AssociationParameters.
147        @return True on success, False otherwise.
148
149        """
150        params = xmlrpc_datatypes.deserialize(raw_params)
151        return self._wifi_proxy.configure_wifi_service(
152                params.ssid,
153                params.security,
154                params.security_parameters,
155                save_credentials=params.save_credentials,
156                station_type=params.station_type,
157                hidden_network=params.is_hidden,
158                guid=params.guid,
159                autoconnect=params.autoconnect)
160
161
162    def connect_wifi(self, raw_params):
163        """Block and attempt to connect to wifi network.
164
165        @param raw_params serialized AssociationParameters.
166        @return serialized AssociationResult
167
168        """
169        logging.debug('connect_wifi()')
170        params = xmlrpc_datatypes.deserialize(raw_params)
171        params.security_config.install_client_credentials(self._tpm_store)
172        wifi_if = params.bgscan_config.interface
173        if wifi_if is None:
174            logging.info('Using default interface for bgscan configuration')
175            interfaces = self.list_controlled_wifi_interfaces()
176            if not interfaces:
177                return xmlrpc_datatypes.AssociationResult(
178                        failure_reason='No wifi interfaces found?')
179
180            if len(interfaces) > 1:
181                logging.error('Defaulting to first interface of %r', interfaces)
182            wifi_if = interfaces[0]
183        if not self._wifi_proxy.configure_bgscan(
184                wifi_if,
185                method=params.bgscan_config.method,
186                short_interval=params.bgscan_config.short_interval,
187                long_interval=params.bgscan_config.long_interval,
188                signal=params.bgscan_config.signal):
189            return xmlrpc_datatypes.AssociationResult(
190                    failure_reason='Failed to configure bgscan')
191
192        raw = self._wifi_proxy.connect_to_wifi_network(
193                params.ssid,
194                params.security,
195                params.security_parameters,
196                params.save_credentials,
197                station_type=params.station_type,
198                hidden_network=params.is_hidden,
199                guid=params.guid,
200                discovery_timeout_seconds=params.discovery_timeout,
201                association_timeout_seconds=params.association_timeout,
202                configuration_timeout_seconds=params.configuration_timeout)
203        result = xmlrpc_datatypes.AssociationResult.from_dbus_proxy_output(raw)
204        return result
205
206
207    @xmlrpc_server.dbus_safe(False)
208    def delete_entries_for_ssid(self, ssid):
209        """Delete a profile entry.
210
211        @param ssid string of WiFi service for which to delete entries.
212        @return True on success, False otherwise.
213
214        """
215        shill = self._wifi_proxy
216        for profile in shill.get_profiles():
217            profile_properties = shill.dbus2primitive(
218                    profile.GetProperties(utf8_strings=True))
219            entry_ids = profile_properties[shill.PROFILE_PROPERTY_ENTRIES]
220            for entry_id in entry_ids:
221                entry = profile.GetEntry(entry_id)
222                if shill.dbus2primitive(entry[shill.ENTRY_FIELD_NAME]) == ssid:
223                    profile.DeleteEntry(entry_id)
224        return True
225
226
227    def init_test_network_state(self):
228        """Create a clean slate for tests with respect to remembered networks.
229
230        For shill, this means popping and removing profiles, removing all WiFi
231        entries from the default profile, and pushing a 'test' profile.
232
233        @return True iff operation succeeded, False otherwise.
234
235        """
236        self.clean_profiles()
237        self._wifi_proxy.remove_all_wifi_entries()
238        self.remove_profile(self.DEFAULT_TEST_PROFILE_NAME)
239        worked = self.create_profile(self.DEFAULT_TEST_PROFILE_NAME)
240        if worked:
241            worked = self.push_profile(self.DEFAULT_TEST_PROFILE_NAME)
242        return worked
243
244
245    @xmlrpc_server.dbus_safe(None)
246    def list_controlled_wifi_interfaces(self):
247        """List WiFi interfaces controlled by shill.
248
249        @return list of string WiFi device names (e.g. ['mlan0'])
250
251        """
252        ret = []
253        devices = self._wifi_proxy.get_devices()
254        for device in devices:
255            properties = self._wifi_proxy.dbus2primitive(
256                    device.GetProperties(utf8_strings=True))
257            if properties[self._wifi_proxy.DEVICE_PROPERTY_TYPE] != 'wifi':
258                continue
259            ret.append(properties[self._wifi_proxy.DEVICE_PROPERTY_NAME])
260        return ret
261
262
263    @xmlrpc_server.dbus_safe(False)
264    def disconnect(self, ssid):
265        """Attempt to disconnect from the given ssid.
266
267        Blocks until disconnected or operation has timed out.  Returns True iff
268        disconnect was successful.
269
270        @param ssid string network to disconnect from.
271        @return bool True on success, False otherwise.
272
273        """
274        logging.debug('disconnect()')
275        result = self._wifi_proxy.disconnect_from_wifi_network(ssid)
276        successful, duration, message = result
277        if successful:
278            level = logging.info
279        else:
280            level = logging.error
281        level('Disconnect result: %r, duration: %d, reason: %s',
282              successful, duration, message)
283        return successful is True
284
285
286    def wait_for_service_states(self, ssid, states, timeout_seconds):
287        """Wait for service to achieve one state out of a list of states.
288
289        @param ssid string the network to connect to (e.g. 'GoogleGuest').
290        @param states tuple the states for which to wait
291        @param timeout_seconds int seconds to wait for a state
292
293        """
294        return self._wifi_proxy.wait_for_service_states(
295                ssid, states, timeout_seconds)
296
297
298    @xmlrpc_server.dbus_safe(None)
299    def get_service_order(self):
300        """Get the shill service order.
301
302        @return string service order on success, None otherwise.
303
304        """
305        return str(self._wifi_proxy.manager.GetServiceOrder())
306
307
308    @xmlrpc_server.dbus_safe(False)
309    def set_service_order(self, order):
310        """Set the shill service order.
311
312        @param order string comma-delimited service order (eg. 'ethernet,wifi')
313        @return bool True on success, False otherwise.
314
315        """
316        self._wifi_proxy.manager.SetServiceOrder(dbus.String(order))
317        return True
318
319
320    @xmlrpc_server.dbus_safe(None)
321    def get_service_properties(self, ssid):
322        """Get a dict of properties for a service.
323
324        @param ssid string service to get properties for.
325        @return dict of Python friendly native types or None on failures.
326
327        """
328        discovery_params = {self._wifi_proxy.SERVICE_PROPERTY_TYPE: 'wifi',
329                            self._wifi_proxy.SERVICE_PROPERTY_NAME: ssid}
330        service_path = self._wifi_proxy.manager.FindMatchingService(
331                discovery_params)
332        service_object = self._wifi_proxy.get_dbus_object(
333                self._wifi_proxy.DBUS_TYPE_SERVICE, service_path)
334        service_properties = service_object.GetProperties(
335                utf8_strings=True)
336        return self._wifi_proxy.dbus2primitive(service_properties)
337
338
339    @xmlrpc_server.dbus_safe(None)
340    def get_manager_properties(self):
341        manager_props = self._wifi_proxy.manager.GetProperties(utf8_strings=True)
342        return self._wifi_proxy.dbus2primitive(manager_props)
343
344
345    @xmlrpc_server.dbus_safe(None)
346    def get_manager_property(self, property_name):
347        prop_value = self._wifi_proxy.get_dbus_property(
348                self._wifi_proxy.manager,  property_name)
349        return self._wifi_proxy.dbus2primitive(prop_value)
350
351
352    @xmlrpc_server.dbus_safe(False)
353    def set_manager_property(self, property_name, property_value):
354        self._wifi_proxy.set_dbus_property(self._wifi_proxy.manager,
355                                           property_name, property_value)
356        return True
357
358    @xmlrpc_server.dbus_safe(False)
359    def set_optional_manager_property(self, property_name, property_value):
360        """Set optional manager property.
361
362        @param property_name String name of property to set
363        @param property_value String value to set property to
364        @return True on success, False otherwise.
365
366        """
367        self._wifi_proxy.set_optional_dbus_property(
368                self._wifi_proxy.manager, property_name, property_value)
369        return True
370
371    @xmlrpc_server.dbus_safe(False)
372    def get_active_wifi_SSIDs(self):
373        """@return list of string SSIDs with at least one BSS we've scanned."""
374        return self._wifi_proxy.get_active_wifi_SSIDs()
375
376
377    @xmlrpc_server.dbus_safe(False)
378    def set_sched_scan(self, enable):
379        """Configure scheduled scan.
380
381        @param enable bool flag indicating to enable/disable scheduled scan.
382        @return True on success, False otherwise.
383
384        """
385        self._wifi_proxy.manager.set_sched_scan(enable)
386        return True
387
388
389    def enable_ui(self):
390        """@return True iff the UI was successfully started."""
391        return cros_ui.start(allow_fail=True, wait_for_login_prompt=False) == 0
392
393
394    def sync_time_to(self, epoch_seconds):
395        """Sync time on the DUT to |epoch_seconds| from the epoch.
396
397        @param epoch_seconds: float number of seconds from the epoch.
398
399        """
400        utils.run('date -u --set=@%f' % epoch_seconds)
401        return True
402
403
404    @staticmethod
405    def do_suspend(seconds):
406        """Suspend DUT using the power manager.
407
408        @param seconds: The number of seconds to suspend the device.
409
410        """
411        return sys_power.do_suspend(seconds)
412
413
414    @staticmethod
415    def do_suspend_bg(seconds):
416        """Suspend DUT using the power manager - non-blocking.
417
418        @param seconds int The number of seconds to suspend the device.
419
420        """
421        process = multiprocessing.Process(target=sys_power.do_suspend,
422                                          args=(seconds, 1))
423        process.start()
424        return True
425
426
427    @xmlrpc_server.dbus_safe(None)
428    def get_dbus_property_on_device(self, wifi_interface, prop_name):
429        """Get a property for the given WiFi device.
430
431        @param wifi_interface: string name of interface being queried.
432        @param prop_name: the name of the property.
433        @return the current value of the property.
434
435        """
436        dbus_object = self._wifi_proxy.find_object(
437                self.DBUS_DEVICE, {'Name': wifi_interface})
438        if dbus_object is None:
439            return None
440
441        object_properties = dbus_object.GetProperties(utf8_strings=True)
442        if prop_name not in object_properties:
443            return None
444
445        return self._wifi_proxy.dbus2primitive(
446                object_properties[prop_name])
447
448
449    @xmlrpc_server.dbus_safe(False)
450    def set_dbus_property_on_device(self, wifi_interface, prop_name, value):
451        """Set a property on the given WiFi device.
452
453        @param wifi_interface: the device to set a property for.
454        @param prop_name: the name of the property.
455        @param value: the desired value of the property.
456        @return True if successful, False otherwise.
457
458        """
459        device_object = self._wifi_proxy.find_object(
460                self.DBUS_DEVICE, {'Name': wifi_interface})
461        if device_object is None:
462            return False
463
464        shill_proxy.ShillProxy.set_dbus_property(device_object,
465                                                 prop_name,
466                                                 value)
467        return True
468
469
470    @xmlrpc_server.dbus_safe(False)
471    def request_roam_dbus(self, bssid, interface):
472        """Request that we roam to the specified BSSID.
473
474        Note that this operation assumes that:
475
476        1) We're connected to an SSID for which |bssid| is a member.
477        2) There is a BSS with an appropriate ID in our scan results.
478
479        @param bssid: string BSSID of BSS to roam to.
480        @param interface: string name of interface to request roam for.
481
482        """
483
484        device_object = self._wifi_proxy.find_object(
485                self.DBUS_DEVICE, {'Name': interface})
486        if device_object is None:
487            return False
488        device_object.RequestRoam(bssid)
489        return True
490
491
492    @xmlrpc_server.dbus_safe(False)
493    def set_device_enabled(self, wifi_interface, enabled):
494        """Enable or disable the WiFi device.
495
496        @param wifi_interface: string name of interface being modified.
497        @param enabled: boolean; true if this device should be enabled,
498                false if this device should be disabled.
499        @return True if it worked; false, otherwise
500
501        """
502        interface = {'Name': wifi_interface}
503        dbus_object = self._wifi_proxy.find_object(self.DBUS_DEVICE,
504                                                   interface)
505        if dbus_object is None:
506            return False
507
508        if enabled:
509            dbus_object.Enable()
510        else:
511            dbus_object.Disable()
512        return True
513
514
515    def discover_tdls_link(self, wifi_interface, peer_mac_address):
516        """Send a TDLS Discover to |peer_mac_address| on |wifi_interface|.
517
518        @param wifi_interface: string name of interface to send the discover on.
519        @param peer_mac_address: string mac address of the TDLS peer device.
520
521        @return True if it the operation was initiated; False otherwise
522
523        """
524        device_object = self._wifi_proxy.find_object(
525                self.DBUS_DEVICE, {'Name': wifi_interface})
526        if device_object is None:
527            return False
528        device_object.PerformTDLSOperation('Discover', peer_mac_address)
529        return True
530
531
532    def establish_tdls_link(self, wifi_interface, peer_mac_address):
533        """Establish a TDLS link with |peer_mac_address| on |wifi_interface|.
534
535        @param wifi_interface: string name of interface to establish a link on.
536        @param peer_mac_address: string mac address of the TDLS peer device.
537
538        @return True if it the operation was initiated; False otherwise
539
540        """
541        device_object = self._wifi_proxy.find_object(
542                self.DBUS_DEVICE, {'Name': wifi_interface})
543        if device_object is None:
544            return False
545        device_object.PerformTDLSOperation('Setup', peer_mac_address)
546        return True
547
548
549    @xmlrpc_server.dbus_safe(False)
550    def query_tdls_link(self, wifi_interface, peer_mac_address):
551        """Query the TDLS link with |peer_mac_address| on |wifi_interface|.
552
553        @param wifi_interface: string name of interface to establish a link on.
554        @param peer_mac_address: string mac address of the TDLS peer device.
555
556        @return string indicating the current TDLS link status.
557
558        """
559        device_object = self._wifi_proxy.find_object(
560                self.DBUS_DEVICE, {'Name': wifi_interface})
561        if device_object is None:
562            return None
563        return self._wifi_proxy.dbus2primitive(
564                device_object.PerformTDLSOperation('Status', peer_mac_address))
565
566
567    @xmlrpc_server.dbus_safe(False)
568    def add_wake_packet_source(self, wifi_interface, source_ip):
569        """Set up the NIC to wake on packets from the given source IP.
570
571        @param wifi_interface: string name of interface to establish WoWLAN on.
572        @param source_ip: string IP address of packet source, i.e. "127.0.0.1"
573
574        @return True on success, False otherwise.
575
576        """
577        device_object = self._wifi_proxy.find_object(
578                self.DBUS_DEVICE, {'Name': wifi_interface})
579        if device_object is None:
580            return False
581        device_object.AddWakeOnPacketConnection(source_ip)
582        return True
583
584
585    @xmlrpc_server.dbus_safe(False)
586    def remove_wake_packet_source(self, wifi_interface, source_ip):
587        """Stop waking on packets from the given source IP.
588
589        @param wifi_interface: string name of interface to establish WoWLAN on.
590        @param source_ip: string IP address of packet source, i.e. "127.0.0.1"
591
592        @return True on success, False otherwise.
593
594        """
595        device_object = self._wifi_proxy.find_object(
596                self.DBUS_DEVICE, {'Name': wifi_interface})
597        if device_object is None:
598            return False
599        device_object.RemoveWakeOnPacketConnection(source_ip)
600        return True
601
602
603    @xmlrpc_server.dbus_safe(False)
604    def remove_all_wake_packet_sources(self, wifi_interface):
605        """Stop waking on packets from any IP.
606
607        @param wifi_interface: string name of interface to establish WoWLAN on.
608
609        @return True on success, False otherwise.
610
611        """
612        device_object = self._wifi_proxy.find_object(
613                self.DBUS_DEVICE, {'Name': wifi_interface})
614        if device_object is None:
615            return False
616        device_object.RemoveAllWakeOnPacketConnections()
617        return True
618
619
620    @xmlrpc_server.dbus_safe(False)
621    def request_scan(self):
622        """Request a scan from shill.
623
624        @return True on success, False otherwise.
625
626        """
627        self._wifi_proxy.manager.RequestScan('wifi')
628        return True
629
630
631
632if __name__ == '__main__':
633    logging.basicConfig(level=logging.DEBUG)
634    handler = logging.handlers.SysLogHandler(address = '/dev/log')
635    formatter = logging.Formatter(
636            'shill_xmlrpc_server: [%(levelname)s] %(message)s')
637    handler.setFormatter(formatter)
638    logging.getLogger().addHandler(handler)
639    logging.debug('shill_xmlrpc_server main...')
640    server = xmlrpc_server.XmlRpcServer('localhost',
641                                         constants.SHILL_XMLRPC_SERVER_PORT)
642    server.register_delegate(ShillXmlRpcDelegate())
643    server.run()
644