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# Lint as: python3
16"""ACTS Wi-Fi Aware Attached test reimplemented in Mobly."""
17import sys
18
19from aware import aware_lib_utils as autils
20from aware import constants
21from mobly import asserts
22from mobly import base_test
23from mobly import records
24from mobly import test_runner
25from mobly import utils
26from mobly.controllers import android_device
27
28
29RUNTIME_PERMISSIONS = (
30    'android.permission.ACCESS_FINE_LOCATION',
31    'android.permission.ACCESS_COARSE_LOCATION',
32    'android.permission.NEARBY_WIFI_DEVICES',
33)
34PACKAGE_NAME = constants.WIFI_AWARE_SNIPPET_PACKAGE_NAME
35
36
37class WifiAwareAttachTest(base_test.BaseTestClass):
38  """Wi-Fi Aware Attach test class."""
39
40  ads: list[android_device.AndroidDevice]
41
42  def setup_class(self):
43    # Register two Android devices.
44    self.ads = self.register_controller(android_device, min_number=1)
45
46    def setup_device(device: android_device.AndroidDevice):
47      device.load_snippet('wifi_aware_snippet', PACKAGE_NAME)
48      for permission in RUNTIME_PERMISSIONS:
49        device.adb.shell(['pm', 'grant', PACKAGE_NAME, permission])
50      asserts.abort_all_if(
51          not device.wifi_aware_snippet.wifiAwareIsAvailable(),
52          f'{device} Wi-Fi Aware is not available.',
53      )
54
55    # Set up devices in parallel.
56    utils.concurrent_exec(
57        setup_device,
58        param_list=[[ad] for ad in self.ads],
59        max_workers=1,
60        raise_on_exception=True,
61    )
62
63  def setup_test(self):
64    for ad in self.ads:
65      autils.control_wifi(ad, True)
66      aware_avail = ad.wifi_aware_snippet.wifiAwareIsAvailable()
67      if not aware_avail:
68        ad.log.info('Aware not available. Waiting ...')
69        state_handler = ad.wifi_aware_snippet.wifiAwareMonitorStateChange()
70        state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_AVAILABLE)
71
72  def teardown_test(self):
73    utils.concurrent_exec(
74        self._teardown_test_on_device,
75        param_list=[[ad] for ad in self.ads],
76        max_workers=1,
77        raise_on_exception=True,
78    )
79    utils.concurrent_exec(
80        lambda d: d.services.create_output_excerpts_all(self.current_test_info),
81        param_list=[[ad] for ad in self.ads],
82        raise_on_exception=True,
83    )
84
85  def _teardown_test_on_device(self, ad: android_device.AndroidDevice) -> None:
86    ad.wifi_aware_snippet.wifiAwareCloseAllWifiAwareSession()
87    ad.wifi_aware_snippet.wifiAwareMonitorStopStateChange()
88    autils.set_airplane_mode(ad, False)
89    autils.control_wifi(ad, True)
90
91  def on_fail(self, record: records.TestResult) -> None:
92    android_device.take_bug_reports(
93        self.ads, destination=self.current_test_info.output_path
94    )
95
96  def test_attach(self) -> None:
97    """Basic attaching request.
98
99    Validates that attaching to the Wi-Fi Aware service works
100    without IdentityChanged callback.
101    """
102    dut = self.ads[0]
103    handler = dut.wifi_aware_snippet.wifiAwareAttached(False)
104    handler.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
105    autils.callback_no_response(
106        handler, constants.AttachCallBackMethodType.ID_CHANGED
107    )
108
109  def test_attach_with_identity(self) -> None:
110    """Basic attaching request with extra callback.
111
112    Validates that attaching to the Wi-Fi Aware service works
113    with IdentityChanged callback.
114    """
115    dut = self.ads[0]
116    handler = dut.wifi_aware_snippet.wifiAwareAttached(True)
117    handler.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
118    handler.waitAndGet(constants.AttachCallBackMethodType.ID_CHANGED)
119
120  def test_attach_multiple_sessions(self):
121    """Multiple attaching request.
122
123    Validates that attaching to the Wi-Fi Aware service works with
124    multiple request at same time.
125    """
126    dut = self.ads[0]
127    handler_1 = dut.wifi_aware_snippet.wifiAwareAttached(False)
128    handler_2 = dut.wifi_aware_snippet.wifiAwareAttached(True)
129    handler_3 = dut.wifi_aware_snippet.wifiAwareAttached(False)
130    handler_1.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
131    autils.callback_no_response(
132        handler_1, constants.AttachCallBackMethodType.ID_CHANGED, 10, True
133    )
134    handler_2.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
135    handler_2.waitAndGet(constants.AttachCallBackMethodType.ID_CHANGED)
136    handler_3.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
137    autils.callback_no_response(
138        handler_3, constants.AttachCallBackMethodType.ID_CHANGED, 10, True
139    )
140
141  def test_attach_with_no_wifi(self):
142    """WiFi Aware attempt to attach with wifi off.
143
144    Validates that if trying to attach with Wi-Fi disabled will receive
145    the expected failure callback. As a side-effect also validates that the
146    broadcast for Aware unavailable is received.
147    """
148    dut = self.ads[0]
149    state_handler = dut.wifi_aware_snippet.wifiAwareMonitorStateChange()
150    autils.control_wifi(dut, False)
151    state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_NOT_AVAILABLE)
152    attach_callback = dut.wifi_aware_snippet.wifiAwareAttached(True)
153    attach_callback.waitAndGet(constants.AttachCallBackMethodType.ATTACH_FAILED)
154    dut.wifi_aware_snippet.wifiAwareMonitorStopStateChange()
155
156  def test_attach_with_location_off(self):
157    """Function/Attach test cases/attempt to attach with location mode off.
158
159    Validates that if trying to attach with device location mode off will
160    receive the expected failure callback. As a side-effect also validates
161    that the broadcast for Aware unavailable is received.
162    """
163    dut = self.ads[0]
164    asserts.skip_if(
165        autils.check_android_os_version(
166            dut, constants.Operator.GREATER_EQUAL, constants.AndroidVersion.T
167        ),
168        'From T build Aware will not be disabled due to location off',
169    )
170    state_handler = dut.wifi_aware_snippet.wifiAwareMonitorStateChange()
171    dut.adb.shell('settings put secure location_mode 0')
172    state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_NOT_AVAILABLE)
173    attach_callback = dut.wifi_aware_snippet.wifiAwareAttached(True)
174    attach_callback.waitAndGet(constants.AttachCallBackMethodType.ATTACH_FAILED)
175    dut.adb.shell('settings put secure location_mode 3')
176    state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_AVAILABLE)
177    dut.wifi_aware_snippet.wifiAwareMonitorStopStateChange()
178
179  def test_attach_apm_toggle_attach_again(self):
180    """Function/Attach test cases/attempt to attach with airplane mode on.
181
182    Validates that enabling Airplane mode while Aware is on resets it
183    correctly, and allows it to be re-enabled when Airplane mode is then
184    disabled.
185    """
186    dut = self.ads[0]
187    asserts.skip_if(
188        not dut.is_adb_root,
189        'APM toggle needs Android device(s) with root permission',
190    )
191    state_handler = dut.wifi_aware_snippet.wifiAwareMonitorStateChange()
192    attach_callback = dut.wifi_aware_snippet.wifiAwareAttached(True)
193    attach_callback.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
194    autils.set_airplane_mode(dut, True)
195    if autils.check_android_os_version(
196        dut, constants.Operator.GREATER_EQUAL, constants.AndroidVersion.T
197    ):
198      if not autils._check_wifi_status(dut):
199        return
200      else:
201        autils.control_wifi(dut, False)
202    autils.callback_no_response(
203        state_handler,
204        constants.WifiAwareBroadcast.WIFI_AWARE_AVAILABLE,
205        10,
206        True,)
207    state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_NOT_AVAILABLE)
208    autils.set_airplane_mode(dut, False)
209    state_handler.waitAndGet(constants.WifiAwareBroadcast.WIFI_AWARE_AVAILABLE)
210    attach_callback = dut.wifi_aware_snippet.wifiAwareAttached(True)
211    attach_callback.waitAndGet(constants.AttachCallBackMethodType.ATTACHED)
212    dut.wifi_aware_snippet.wifiAwareMonitorStopStateChange()
213
214
215if __name__ == '__main__':
216  # Take test args
217  if '--' in sys.argv:
218    index = sys.argv.index('--')
219    sys.argv = sys.argv[:1] + sys.argv[index + 1 :]
220
221  test_runner.main()
222