1#  Copyright (C) 2024 The Android Open Source Project
2#
3#  Licensed under the Apache License, Version 2.0 (the "License");
4#  you may not use this file except in compliance with the License.
5#  You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#  Unless required by applicable law or agreed to in writing, software
10#  distributed under the License is distributed on an "AS IS" BASIS,
11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#  See the License for the specific language governing permissions and
13#  limitations under the License.
14
15import base64
16import uuid
17
18from mobly import asserts
19from mobly.controllers import android_device
20
21
22class UpstreamType:
23  NONE = 0
24  CELLULAR = 1
25  WIFI = 2
26
27
28def generate_uuid32_base64() -> str:
29  """Generates a UUID32 and encodes it in Base64.
30
31  Returns:
32      str: The Base64-encoded UUID32 string. Which is 22 characters.
33  """
34  # Strip padding characters to make it safer for hotspot name length limit.
35  return base64.b64encode(uuid.uuid1().bytes).decode("utf-8").strip("=")
36
37
38def assume_hotspot_test_preconditions(
39    server_device: android_device,
40    client_device: android_device,
41    upstream_type: UpstreamType,
42) -> None:
43  server = server_device.connectivity_multi_devices_snippet
44  client = client_device.connectivity_multi_devices_snippet
45
46  # Assert pre-conditions specific to each upstream type.
47  asserts.skip_if(not client.hasWifiFeature(), "Client requires Wifi feature")
48  asserts.skip_if(
49      not server.hasHotspotFeature(), "Server requires hotspot feature"
50  )
51  if upstream_type == UpstreamType.CELLULAR:
52    asserts.skip_if(
53        not server.hasTelephonyFeature(), "Server requires Telephony feature"
54    )
55  elif upstream_type == UpstreamType.WIFI:
56    asserts.skip_if(
57        not server.isStaApConcurrencySupported(),
58        "Server requires Wifi AP + STA concurrency",
59    )
60  elif upstream_type == UpstreamType.NONE:
61    pass
62  else:
63    raise ValueError(f"Invalid upstream type: {upstream_type}")
64
65
66def setup_hotspot_and_client_for_upstream_type(
67    server_device: android_device,
68    client_device: android_device,
69    upstream_type: UpstreamType,
70) -> (str, int):
71  """Setup the hotspot with a connected client with the specified upstream type.
72
73  This creates a hotspot, make the client connect
74  to it, and verify the packet is forwarded by the hotspot.
75  And returns interface name of both if successful.
76  """
77  server = server_device.connectivity_multi_devices_snippet
78  client = client_device.connectivity_multi_devices_snippet
79
80  if upstream_type == UpstreamType.CELLULAR:
81    server.requestCellularAndEnsureDefault()
82  elif upstream_type == UpstreamType.WIFI:
83    server.ensureWifiIsDefault()
84  elif upstream_type == UpstreamType.NONE:
85    pass
86  else:
87    raise ValueError(f"Invalid upstream type: {upstream_type}")
88
89  # Generate ssid/passphrase with random characters to make sure nearby devices won't
90  # connect unexpectedly. Note that total length of ssid cannot go over 32.
91  test_ssid = "HOTSPOT-" + generate_uuid32_base64()
92  test_passphrase = generate_uuid32_base64()
93
94  # Create a hotspot with fixed SSID and password.
95  hotspot_interface = server.startHotspot(test_ssid, test_passphrase)
96
97  # Make the client connects to the hotspot.
98  client_network = client.connectToWifi(test_ssid, test_passphrase)
99
100  return hotspot_interface, client_network
101
102
103def cleanup_tethering_for_upstream_type(
104    server_device: android_device, upstream_type: UpstreamType
105) -> None:
106  server = server_device.connectivity_multi_devices_snippet
107  if upstream_type == UpstreamType.CELLULAR:
108    server.unregisterAll()
109  # Teardown the hotspot.
110  server.stopAllTethering()
111  # Some test cases would disable wifi, e.g. cellular upstream tests.
112  # Reconnect to it if feasible.
113  server.reconnectWifiIfSupported()
114