| # Copyright (C) 2024 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import base64 | |
| import uuid | |
| from mobly import asserts | |
| from mobly.controllers import android_device | |
| class UpstreamType: | |
| NONE = 0 | |
| CELLULAR = 1 | |
| WIFI = 2 | |
| def generate_uuid32_base64() -> str: | |
| """Generates a UUID32 and encodes it in Base64. | |
| Returns: | |
| str: The Base64-encoded UUID32 string. Which is 22 characters. | |
| """ | |
| # Strip padding characters to make it safer for hotspot name length limit. | |
| return base64.b64encode(uuid.uuid1().bytes).decode("utf-8").strip("=") | |
| def assume_hotspot_test_preconditions( | |
| server_device: android_device, | |
| client_device: android_device, | |
| upstream_type: UpstreamType, | |
| ) -> None: | |
| server = server_device.connectivity_multi_devices_snippet | |
| client = client_device.connectivity_multi_devices_snippet | |
| # Assert pre-conditions specific to each upstream type. | |
| asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature") | |
| asserts.skip_if( | |
| not server.hasHotspotFeature(), "Server requires hotspot feature" | |
| ) | |
| if upstream_type == UpstreamType.CELLULAR: | |
| asserts.skip_if( | |
| not server.hasTelephonyFeature(), "Server requires Telephony feature" | |
| ) | |
| elif upstream_type == UpstreamType.WIFI: | |
| asserts.skip_if( | |
| not server.isStaApConcurrencySupported(), | |
| "Server requires Wifi AP + STA concurrency", | |
| ) | |
| elif upstream_type == UpstreamType.NONE: | |
| pass | |
| else: | |
| raise ValueError(f"Invalid upstream type: {upstream_type}") | |
| def setup_hotspot_and_client_for_upstream_type( | |
| server_device: android_device, | |
| client_device: android_device, | |
| upstream_type: UpstreamType, | |
| ) -> (str, int): | |
| """Setup the hotspot with a connected client with the specified upstream type. | |
| This creates a hotspot, make the client connect | |
| to it, and verify the packet is forwarded by the hotspot. | |
| And returns interface name of both if successful. | |
| """ | |
| server = server_device.connectivity_multi_devices_snippet | |
| client = client_device.connectivity_multi_devices_snippet | |
| if upstream_type == UpstreamType.CELLULAR: | |
| server.requestCellularAndEnsureDefault() | |
| elif upstream_type == UpstreamType.WIFI: | |
| server.ensureWifiIsDefault() | |
| elif upstream_type == UpstreamType.NONE: | |
| pass | |
| else: | |
| raise ValueError(f"Invalid upstream type: {upstream_type}") | |
| # Generate ssid/passphrase with random characters to make sure nearby devices won't | |
| # connect unexpectedly. Note that total length of ssid cannot go over 32. | |
| test_ssid = "HOTSPOT-" + generate_uuid32_base64() | |
| test_passphrase = generate_uuid32_base64() | |
| # Create a hotspot with fixed SSID and password. | |
| hotspot_interface = server.startHotspot(test_ssid, test_passphrase) | |
| # Make the client connects to the hotspot. | |
| client_network = client.connectToWifi(test_ssid, test_passphrase) | |
| return hotspot_interface, client_network | |
| def cleanup_tethering_for_upstream_type( | |
| server_device: android_device, upstream_type: UpstreamType | |
| ) -> None: | |
| server = server_device.connectivity_multi_devices_snippet | |
| if upstream_type == UpstreamType.CELLULAR: | |
| server.unregisterAll() | |
| # Teardown the hotspot. | |
| server.stopAllTethering() |