1#!/usr/bin/env python3.4
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import itertools
18import pprint
19import queue
20import time
21
22import acts.base_test
23import acts.test_utils.wifi.wifi_test_utils as wutils
24
25
26import WifiManagerTest
27from acts import asserts
28from acts import signals
29from acts.libs.uicd.uicd_cli import UicdCli
30from acts.libs.uicd.uicd_cli import UicdError
31from acts.test_decorators import test_tracker_info
32from acts.test_utils.tel.tel_test_utils import get_operator_name
33from acts.utils import force_airplane_mode
34
35WifiEnums = wutils.WifiEnums
36
37DEFAULT_TIMEOUT = 10
38OSU_TEST_TIMEOUT = 300
39
40# Constants for providers.
41GLOBAL_RE = 0
42OSU_BOINGO = 0
43BOINGO = 1
44ATT = 2
45
46# Constants used for various device operations.
47RESET = 1
48TOGGLE = 2
49
50UNKNOWN_FQDN = "@#@@!00fffffx"
51
52class WifiPasspointTest(acts.base_test.BaseTestClass):
53    """Tests for APIs in Android's WifiManager class.
54
55    Test Bed Requirement:
56    * One Android device
57    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
58      network.
59    """
60
61    def setup_class(self):
62        self.dut = self.android_devices[0]
63        wutils.wifi_test_device_init(self.dut)
64        req_params = ["passpoint_networks", "uicd_workflows", "uicd_zip"]
65        opt_param = []
66        self.unpack_userparams(
67            req_param_names=req_params, opt_param_names=opt_param)
68        self.unpack_userparams(req_params)
69        asserts.assert_true(
70            len(self.passpoint_networks) > 0,
71            "Need at least one Passpoint network.")
72        wutils.wifi_toggle_state(self.dut, True)
73        self.unknown_fqdn = UNKNOWN_FQDN
74        # Setup Uicd cli object for UI interation.
75        self.ui = UicdCli(self.uicd_zip[0], self.uicd_workflows)
76        self.passpoint_workflow = "passpoint-login_%s" % self.dut.model
77
78
79    def setup_test(self):
80        self.dut.droid.wakeLockAcquireBright()
81        self.dut.droid.wakeUpNow()
82        self.dut.unlock_screen()
83
84
85    def teardown_test(self):
86        self.dut.droid.wakeLockRelease()
87        self.dut.droid.goToSleepNow()
88        wutils.reset_wifi(self.dut)
89
90
91    def on_fail(self, test_name, begin_time):
92        self.dut.take_bug_report(test_name, begin_time)
93
94
95    """Helper Functions"""
96
97
98    def install_passpoint_profile(self, passpoint_config):
99        """Install the Passpoint network Profile.
100
101        Args:
102            passpoint_config: A JSON dict of the Passpoint configuration.
103
104        """
105        asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config,
106                "Key '%s' must be present in network definition." %
107                WifiEnums.SSID_KEY)
108        # Install the Passpoint profile.
109        self.dut.droid.addUpdatePasspointConfig(passpoint_config)
110
111
112    def check_passpoint_connection(self, passpoint_network):
113        """Verify the device is automatically able to connect to the Passpoint
114           network.
115
116           Args:
117               passpoint_network: SSID of the Passpoint network.
118
119        """
120        ad = self.dut
121        ad.ed.clear_all_events()
122        wutils.start_wifi_connection_scan(ad)
123        scan_results = ad.droid.wifiGetScanResults()
124        # Wait for scan to complete.
125        time.sleep(5)
126        ssid = passpoint_network
127        wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, scan_results)
128        # Passpoint network takes longer time to connect than normal networks.
129        # Every try comes with a timeout of 30s. Setting total timeout to 120s.
130        wutils.wifi_passpoint_connect(self.dut, passpoint_network, num_of_tries=4)
131        # Re-verify we are connected to the correct network.
132        network_info = self.dut.droid.wifiGetConnectionInfo()
133        if network_info[WifiEnums.SSID_KEY] != passpoint_network:
134            raise signals.TestFailure("Device did not connect to the passpoint"
135                                      " network.")
136
137
138    def get_configured_passpoint_and_delete(self):
139        """Get configured Passpoint network and delete using its FQDN."""
140        passpoint_config = self.dut.droid.getPasspointConfigs()
141        if not len(passpoint_config):
142            raise signals.TestFailure("Failed to fetch the list of configured"
143                                      "passpoint networks.")
144        if not wutils.delete_passpoint(self.dut, passpoint_config[0]):
145            raise signals.TestFailure("Failed to delete Passpoint configuration"
146                                      " with FQDN = %s" % passpoint_config[0])
147
148    def start_subscription_provisioning(self, state):
149        """Start subscription provisioning with a default provider."""
150
151        self.unpack_userparams(('osu_configs',))
152        asserts.assert_true(
153            len(self.osu_configs) > 0,
154            "Need at least one osu config.")
155        osu_config = self.osu_configs[OSU_BOINGO]
156        # Clear all previous events.
157        self.dut.ed.clear_all_events()
158        self.dut.droid.startSubscriptionProvisioning(osu_config)
159        start_time = time.time()
160        while time.time() < start_time + OSU_TEST_TIMEOUT:
161            dut_event = self.dut.ed.pop_event("onProvisioningCallback",
162                                              DEFAULT_TIMEOUT * 18)
163            if dut_event['data']['tag'] == 'success':
164                self.log.info("Passpoint Provisioning Success")
165                # Reset WiFi after provisioning success.
166                if state == RESET:
167                    wutils.reset_wifi(self.dut)
168                    time.sleep(DEFAULT_TIMEOUT)
169                # Toggle WiFi after provisioning success.
170                elif state == TOGGLE:
171                    wutils.toggle_wifi_off_and_on(self.dut)
172                    time.sleep(DEFAULT_TIMEOUT)
173                break
174            if dut_event['data']['tag'] == 'failure':
175                raise signals.TestFailure(
176                    "Passpoint Provisioning is failed with %s" %
177                    dut_event['data'][
178                        'reason'])
179                break
180            if dut_event['data']['tag'] == 'status':
181                self.log.info(
182                    "Passpoint Provisioning status %s" % dut_event['data'][
183                        'status'])
184                if int(dut_event['data']['status']) == 7:
185                    self.ui.run(self.dut.serial, self.passpoint_workflow)
186        # Clear all previous events.
187        self.dut.ed.clear_all_events()
188
189        # Verify device connects to the Passpoint network.
190        time.sleep(DEFAULT_TIMEOUT)
191        current_passpoint = self.dut.droid.wifiGetConnectionInfo()
192        if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[
193            "expected_ssids"]:
194            raise signals.TestFailure("Device did not connect to the %s"
195                                      " passpoint network" % osu_config[
196                                          "expected_ssids"])
197        # Delete the Passpoint profile.
198        self.get_configured_passpoint_and_delete()
199        wutils.wait_for_disconnect(self.dut)
200
201
202    """Tests"""
203
204    @test_tracker_info(uuid="b0bc0153-77bb-4594-8f19-cea2c6bd2f43")
205    def test_add_passpoint_network(self):
206        """Add a Passpoint network and verify device connects to it.
207
208        Steps:
209            1. Install a Passpoint Profile.
210            2. Verify the device connects to the required Passpoint SSID.
211            3. Get the Passpoint configuration added above.
212            4. Delete Passpoint configuration using its FQDN.
213            5. Verify that we are disconnected from the Passpoint network.
214
215        """
216        passpoint_config = self.passpoint_networks[BOINGO]
217        self.install_passpoint_profile(passpoint_config)
218        ssid = passpoint_config[WifiEnums.SSID_KEY]
219        self.check_passpoint_connection(ssid)
220        self.get_configured_passpoint_and_delete()
221        wutils.wait_for_disconnect(self.dut)
222
223
224    @test_tracker_info(uuid="eb29d6e2-a755-4c9c-9e4e-63ea2277a64a")
225    def test_update_passpoint_network(self):
226        """Update a previous Passpoint network and verify device still connects
227           to it.
228
229        1. Install a Passpoint Profile.
230        2. Verify the device connects to the required Passpoint SSID.
231        3. Update the Passpoint Profile.
232        4. Verify device is still connected to the Passpoint SSID.
233        5. Get the Passpoint configuration added above.
234        6. Delete Passpoint configuration using its FQDN.
235
236        """
237        passpoint_config = self.passpoint_networks[BOINGO]
238        self.install_passpoint_profile(passpoint_config)
239        ssid = passpoint_config[WifiEnums.SSID_KEY]
240        self.check_passpoint_connection(ssid)
241
242        # Update passpoint configuration using the original profile because we
243        # do not have real profile with updated credentials to use.
244        self.install_passpoint_profile(passpoint_config)
245
246        # Wait for a Disconnect event from the supplicant.
247        wutils.wait_for_disconnect(self.dut)
248
249        # Now check if we are again connected with the updated profile.
250        self.check_passpoint_connection(ssid)
251
252        self.get_configured_passpoint_and_delete()
253        wutils.wait_for_disconnect(self.dut)
254
255
256    @test_tracker_info(uuid="b6e8068d-faa1-49f2-b421-c60defaed5f0")
257    def test_add_delete_list_of_passpoint_network(self):
258        """Add multiple passpoint networks, list them and delete one by one.
259
260        1. Install Passpoint Profile A.
261        2. Install Passpoint Profile B.
262        3. Get all the Passpoint configurations added above and verify.
263        6. Ensure all Passpoint configurations can be deleted.
264
265        """
266        for passpoint_config in self.passpoint_networks[:2]:
267            self.install_passpoint_profile(passpoint_config)
268            time.sleep(DEFAULT_TIMEOUT)
269        configs = self.dut.droid.getPasspointConfigs()
270        #  It is length -1 because ATT profile will be handled separately
271        if not len(configs) or len(configs) != len(self.passpoint_networks[:2]):
272            raise signals.TestFailure("Failed to fetch some or all of the"
273                                      " configured passpoint networks.")
274        for config in configs:
275            if not wutils.delete_passpoint(self.dut, config):
276                raise signals.TestFailure("Failed to delete Passpoint"
277                                          " configuration with FQDN = %s" %
278                                          config)
279
280
281    @test_tracker_info(uuid="a53251be-7aaf-41fc-a5f3-63984269d224")
282    def test_delete_unknown_fqdn(self):
283        """Negative test to delete Passpoint profile using an unknown FQDN.
284
285        1. Pass an unknown FQDN for removal.
286        2. Verify that it was not successful.
287
288        """
289        if wutils.delete_passpoint(self.dut, self.unknown_fqdn):
290            raise signals.TestFailure("Failed because an unknown FQDN"
291                                      " was successfully deleted.")
292
293
294    @test_tracker_info(uuid="bf03c03a-e649-4e2b-a557-1f791bd98951")
295    def test_passpoint_failover(self):
296        """Add a pair of passpoint networks and test failover when one of the"
297           profiles is removed.
298
299        1. Install a Passpoint Profile A and B.
300        2. Verify device connects to a Passpoint network and get SSID.
301        3. Delete the current Passpoint profile using its FQDN.
302        4. Verify device fails over and connects to the other Passpoint SSID.
303        5. Delete Passpoint configuration using its FQDN.
304
305        """
306        # Install both Passpoint profiles on the device.
307        passpoint_ssid = list()
308        for passpoint_config in self.passpoint_networks[:2]:
309            passpoint_ssid.append(passpoint_config[WifiEnums.SSID_KEY])
310            self.install_passpoint_profile(passpoint_config)
311            time.sleep(DEFAULT_TIMEOUT)
312
313        # Get the current network and the failover network.
314        wutils.wait_for_connect(self.dut)
315        current_passpoint = self.dut.droid.wifiGetConnectionInfo()
316        current_ssid = current_passpoint[WifiEnums.SSID_KEY]
317        if current_ssid not in passpoint_ssid:
318           raise signals.TestFailure("Device did not connect to any of the "
319                                     "configured Passpoint networks.")
320
321        expected_ssid =  self.passpoint_networks[0][WifiEnums.SSID_KEY]
322        if current_ssid == expected_ssid:
323            expected_ssid = self.passpoint_networks[1][WifiEnums.SSID_KEY]
324
325        # Remove the current Passpoint profile.
326        for network in self.passpoint_networks[:2]:
327            if network[WifiEnums.SSID_KEY] == current_ssid:
328                if not wutils.delete_passpoint(self.dut, network["fqdn"]):
329                    raise signals.TestFailure("Failed to delete Passpoint"
330                                              " configuration with FQDN = %s" %
331                                              network["fqdn"])
332        # Verify device fails over and connects to the other passpoint network.
333        time.sleep(DEFAULT_TIMEOUT)
334
335        current_passpoint = self.dut.droid.wifiGetConnectionInfo()
336        if current_passpoint[WifiEnums.SSID_KEY] != expected_ssid:
337            raise signals.TestFailure("Device did not failover to the %s"
338                                      " passpoint network" % expected_ssid)
339
340        # Delete the remaining Passpoint profile.
341        self.get_configured_passpoint_and_delete()
342        wutils.wait_for_disconnect(self.dut)
343
344
345    def test_install_att_passpoint_profile(self):
346        """Add an AT&T Passpoint profile.
347
348        It is used for only installing the profile for other tests.
349        """
350        isFound = False
351        for passpoint_config in self.passpoint_networks:
352            if 'att' in passpoint_config['fqdn']:
353                isFound = True
354                self.install_passpoint_profile(passpoint_config)
355                break
356        if not isFound:
357            raise signals.TestFailure("cannot find ATT profile.")
358
359
360    @test_tracker_info(uuid="e3e826d2-7c39-4c37-ab3f-81992d5aa0e8")
361    def test_att_passpoint_network(self):
362        """Add a AT&T Passpoint network and verify device connects to it.
363
364        Steps:
365            1. Install a AT&T Passpoint Profile.
366            2. Verify the device connects to the required Passpoint SSID.
367            3. Get the Passpoint configuration added above.
368            4. Delete Passpoint configuration using its FQDN.
369            5. Verify that we are disconnected from the Passpoint network.
370
371        """
372        carriers = ["att"]
373        operator = get_operator_name(self.log, self.dut)
374        asserts.skip_if(operator not in carriers,
375                        "Device %s does not have a ATT sim" % self.dut.model)
376
377        passpoint_config = self.passpoint_networks[ATT]
378        self.install_passpoint_profile(passpoint_config)
379        ssid = passpoint_config[WifiEnums.SSID_KEY]
380        self.check_passpoint_connection(ssid)
381        self.get_configured_passpoint_and_delete()
382        wutils.wait_for_disconnect(self.dut)
383
384
385    @test_tracker_info(uuid="c85c81b2-7133-4635-8328-9498169ae802")
386    def test_start_subscription_provisioning(self):
387        self.start_subscription_provisioning(0)
388
389
390    @test_tracker_info(uuid="fd09a643-0d4b-45a9-881a-a771f9707ab1")
391    def test_start_subscription_provisioning_and_reset_wifi(self):
392        self.start_subscription_provisioning(RESET)
393
394
395    @test_tracker_info(uuid="f43ea759-673f-4567-aa11-da3bc2cabf08")
396    def test_start_subscription_provisioning_and_toggle_wifi(self):
397        self.start_subscription_provisioning(TOGGLE)
398