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