1#  Copyright (C) 2023 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"""Constants for Nearby Connection."""
16
17import dataclasses
18import datetime
19import enum
20
21NEARBY_RESET_WAIT_TIME = datetime.timedelta(seconds=5)
22WIFI_DISCONNECTION_DELAY = datetime.timedelta(seconds=3)
23
24FIRST_DISCOVERY_TIMEOUT = datetime.timedelta(seconds=30)
25FIRST_CONNECTION_INIT_TIMEOUT = datetime.timedelta(seconds=30)
26FIRST_CONNECTION_RESULT_TIMEOUT = datetime.timedelta(seconds=35)
27FILE_1M_PAYLOAD_TRANSFER_TIMEOUT = datetime.timedelta(seconds=110)
28SECOND_DISCOVERY_TIMEOUT = datetime.timedelta(seconds=35)
29SECOND_CONNECTION_INIT_TIMEOUT = datetime.timedelta(seconds=10)
30SECOND_CONNECTION_RESULT_TIMEOUT = datetime.timedelta(seconds=25)
31CONNECTION_BANDWIDTH_CHANGED_TIMEOUT = datetime.timedelta(seconds=25)
32FILE_1G_PAYLOAD_TRANSFER_TIMEOUT = datetime.timedelta(seconds=400)
33WIFI_WLAN_CONNECTING_TIME_OUT = datetime.timedelta(seconds=25)
34DISCONNECTION_TIMEOUT = datetime.timedelta(seconds=15)
35
36BT_TRANSFER_THROUGHPUT_MEDIAN_BENCHMARK_KBPS = 20  # 20KBps
37WIFI_TRANSFER_THROUGHPUT_MEDIAN_BENCHMARK_KBPS = 10240  # 10MBps
38BT_TRANSFER_SUCCESS_RATE_TARGET_PERCENTAGE = 95  # 95%
39WIFI_TRANSFER_SUCCESS_RATE_TARGET_PERCENTAGE = 95  # 95%
40
41KEEP_ALIVE_TIMEOUT_BT_MS = 30000
42KEEP_ALIVE_INTERVAL_BT_MS = 5000
43
44KEEP_ALIVE_TIMEOUT_WIFI_MS = 10000
45KEEP_ALIVE_INTERVAL_WIFI_MS = 3000
46
47PERCENTILE_50_FACTOR = 0.5
48PERCENTILE_95_FACTOR = 0.95
49LATENCY_PRECISION_DIGITS = 1
50SUCCESS_RATE_PRECISION_DIGITS = 1
51
52UNSET_LATENCY = datetime.timedelta.max
53UNSET_THROUGHPUT_KBPS = -1.0
54MAX_NUM_BUG_REPORT = 5
55TARGET_POST_WIFI_CONNECTION_IDLE_TIME_SEC = 0
56
57
58@enum.unique
59class PayloadType(enum.IntEnum):
60  FILE = 2
61  STREAM = 3
62
63
64@enum.unique
65class NearbyMedium(enum.IntEnum):
66  """Medium options for discovery, advertising, connection and upgrade."""
67
68  AUTO = 0
69  BT_ONLY = 1
70  BLE_ONLY = 2
71  WIFILAN_ONLY = 3
72  WIFIAWARE_ONLY = 4
73  UPGRADE_TO_WEBRTC = 5
74  UPGRADE_TO_WIFIHOTSPOT = 6
75  UPGRADE_TO_WIFIDIRECT = 7
76  BLE_L2CAP_ONLY = 8
77  # including WIFI_WLAN, WIFI_HOTSPOT, WIFI_DIRECT
78  UPGRADE_TO_ALL_WIFI = 9
79
80
81@dataclasses.dataclass(frozen=False)
82class TestParameters:
83  """Test parameters to be customized for Nearby Connection."""
84
85  test_report_alias_name: str = 'unspecified'
86  fast_fail_on_any_error: bool = False
87  wifi_country_code: str = ''
88  wifi_ssid: str = ''
89  wifi_password: str = ''
90  toggle_airplane_mode_target_side: bool = True
91  reset_wifi_connection: bool = True
92  disconnect_bt_after_test: bool = False
93  disconnect_wifi_after_test: bool = False
94  bt_transfer_throughput_median_benchmark_kbps: float = (
95      BT_TRANSFER_THROUGHPUT_MEDIAN_BENCHMARK_KBPS
96  )
97  wifi_transfer_throughput_median_benchmark_kbps: float = (
98      WIFI_TRANSFER_THROUGHPUT_MEDIAN_BENCHMARK_KBPS
99  )
100  payload_type: PayloadType = PayloadType.FILE
101  advertising_discovery_medium: int = NearbyMedium.BLE_ONLY
102  connection_medium: int = NearbyMedium.BT_ONLY
103  upgrade_medium: int = NearbyMedium.UPGRADE_TO_ALL_WIFI
104  allow_unrooted_device: bool = False
105  keep_alive_timeout_ms: int = KEEP_ALIVE_TIMEOUT_WIFI_MS
106  keep_alive_interval_ms: int = KEEP_ALIVE_INTERVAL_WIFI_MS
107  target_post_wifi_connection_idle_time_sec: int = (
108      TARGET_POST_WIFI_CONNECTION_IDLE_TIME_SEC
109  )
110
111
112@enum.unique
113class NearbyConnectionMedium(enum.IntEnum):
114  """The final connection medium selected, see BandWidthInfo.Medium."""
115  UNKNOWN = 0
116  # reserved 1, it's Medium.MDNS, not used now
117  BLUETOOTH = 2
118  WIFI_HOTSPOT = 3
119  BLE = 4
120  WIFI_LAN = 5
121  WIFI_AWARE = 6
122  NFC = 7
123  WIFI_DIRECT = 8
124  WEB_RTC = 9
125  # 10 is reserved.
126  USB = 11
127
128
129def is_high_quality_medium(medium: NearbyMedium) -> bool:
130  return medium in {
131      NearbyMedium.WIFILAN_ONLY,
132      NearbyMedium.WIFIAWARE_ONLY,
133      NearbyMedium.UPGRADE_TO_WEBRTC,
134      NearbyMedium.UPGRADE_TO_WIFIHOTSPOT,
135      NearbyMedium.UPGRADE_TO_WIFIDIRECT,
136      NearbyMedium.UPGRADE_TO_ALL_WIFI,
137  }
138
139
140@enum.unique
141class MediumUpgradeType(enum.IntEnum):
142  DEFAULT = 0
143  DISRUPTIVE = 1
144  NON_DISRUPTIVE = 2
145
146
147@dataclasses.dataclass(frozen=True)
148class ConnectionSetupTimeouts:
149  """The timeouts of the nearby connection setup."""
150  discovery_timeout: datetime.timedelta | None = None
151  connection_init_timeout: datetime.timedelta | None = None
152  connection_result_timeout: datetime.timedelta | None = None
153
154
155@dataclasses.dataclass(frozen=False)
156class ConnectionSetupQualityInfo:
157  """The quality information of the nearby connection setup."""
158  discovery_latency: datetime.timedelta = UNSET_LATENCY
159  connection_latency: datetime.timedelta = UNSET_LATENCY
160  medium_upgrade_latency: datetime.timedelta = UNSET_LATENCY
161  medium_upgrade_expected: bool = False
162  upgrade_medium: NearbyConnectionMedium | None = None
163
164  def get_dict(self):
165    dict_repr = {
166        'discovery': f'{round(self.discovery_latency.total_seconds(), 1)}s',
167        'connection': f'{round(self.connection_latency.total_seconds(), 1)}s'
168    }
169    if self.medium_upgrade_expected:
170      dict_repr['upgrade'] = (
171          f'{round(self.medium_upgrade_latency.total_seconds(), 1)}s'
172      )
173    if self.upgrade_medium:
174      dict_repr['medium'] = self.upgrade_medium.name
175    return dict_repr
176
177
178@dataclasses.dataclass(frozen=False)
179class SingleTestResult:
180  """The test result of a single iteration."""
181
182  connection_setup_quality_info: ConnectionSetupQualityInfo = (
183      dataclasses.field(default_factory=ConnectionSetupQualityInfo)
184  )
185  bt_transfer_throughput_kbps: float = UNSET_THROUGHPUT_KBPS
186  discoverer_wifi_wlan_latency: datetime.timedelta = UNSET_LATENCY
187  second_connection_setup_quality_info: ConnectionSetupQualityInfo = (
188      dataclasses.field(default_factory=ConnectionSetupQualityInfo)
189  )
190  wifi_transfer_throughput_kbps: float = UNSET_THROUGHPUT_KBPS
191  advertiser_wifi_wlan_latency: datetime.timedelta = UNSET_LATENCY
192  discoverer_wifi_wlan_expected: bool = False
193  advertiser_wifi_wlan_expected: bool = False
194
195
196@dataclasses.dataclass(frozen=True)
197class LatencyResultStats:
198  average_latency: float
199  percentile_50: float
200  percentile_95: float
201  failure_count: int
202
203
204@dataclasses.dataclass(frozen=True)
205class FailTargetSummary:
206  title: str = ''
207  actual: float = 0.0
208  goal: float = 0.0
209  unit: str = ''
210
211
212@dataclasses.dataclass(frozen=True)
213class ThroughputResultStats:
214  success_rate: float
215  average_kbps: float
216  percentile_50_kbps: float
217  percentile_95_kbps: float
218  success_count: int
219  fail_targets: list[FailTargetSummary] = dataclasses.field(
220      default_factory=list)
221
222
223@dataclasses.dataclass(frozen=False)
224class QuickStartTestMetrics:
225  """Metrics data for quick start test."""
226  first_discovery_latencies: list[datetime.timedelta] = dataclasses.field(
227      default_factory=list[datetime.timedelta])
228  first_connection_latencies: list[datetime.timedelta] = dataclasses.field(
229      default_factory=list[datetime.timedelta])
230  discoverer_wifi_wlan_latencies: list[
231      datetime.timedelta] = dataclasses.field(
232          default_factory=list[datetime.timedelta])
233  bt_transfer_throughputs_kbps: list[float] = dataclasses.field(
234      default_factory=list[float])
235  second_discovery_latencies: list[datetime.timedelta] = dataclasses.field(
236      default_factory=list[datetime.timedelta]
237  )
238  second_connection_latencies: list[datetime.timedelta] = dataclasses.field(
239      default_factory=list[datetime.timedelta])
240  medium_upgrade_latencies: list[
241      datetime.timedelta] = dataclasses.field(
242          default_factory=list[datetime.timedelta])
243  advertiser_wifi_wlan_latencies: list[
244      datetime.timedelta] = dataclasses.field(
245          default_factory=list[datetime.timedelta])
246  wifi_transfer_throughputs_kbps: list[float] = dataclasses.field(
247      default_factory=list[float])
248  upgraded_wifi_transfer_mediums: list[NearbyConnectionMedium] = (
249      dataclasses.field(default_factory=list[NearbyConnectionMedium]))
250