• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.cts.statsdatom.wifi;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import android.cts.statsdatom.lib.AtomTestUtils;
23 import android.cts.statsdatom.lib.ConfigUtils;
24 import android.cts.statsdatom.lib.DeviceUtils;
25 import android.cts.statsdatom.lib.ReportUtils;
26 import android.net.wifi.WifiModeEnum;
27 
28 import com.android.compatibility.common.util.ApiLevelUtil;
29 import com.android.os.AtomsProto;
30 import com.android.os.StatsLog;
31 import com.android.tradefed.build.IBuildInfo;
32 import com.android.tradefed.testtype.DeviceTestCase;
33 import com.android.tradefed.testtype.IBuildReceiver;
34 import com.android.tradefed.util.RunUtil;
35 
36 import com.google.common.collect.Range;
37 import com.google.protobuf.AbstractMessage;
38 
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.function.Predicate;
45 import java.util.stream.Collectors;
46 
47 public class WifiStatsTests extends DeviceTestCase implements IBuildReceiver {
48     private IBuildInfo mCtsBuild;
49 
50     private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000;
51     private static final String FEATURE_PC = "android.hardware.type.pc";
52     private static final String FEATURE_WIFI = "android.hardware.wifi";
53     private static final String FEATURE_NAMESPACE = "wifi";
54     private String mDefaultValue;
55 
56     @Override
setUp()57     protected void setUp() throws Exception {
58         super.setUp();
59         assertThat(mCtsBuild).isNotNull();
60         ConfigUtils.removeConfig(getDevice());
61         ReportUtils.clearReports(getDevice());
62         DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
63         saveFeature("high_perf_lock_deprecated");
64         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
65     }
66 
67     @Override
tearDown()68     protected void tearDown() throws Exception {
69         restoreFeature("high_perf_lock_deprecated");
70         ConfigUtils.removeConfig(getDevice());
71         ReportUtils.clearReports(getDevice());
72         DeviceUtils.uninstallStatsdTestApp(getDevice());
73         super.tearDown();
74     }
75 
76     @Override
setBuild(IBuildInfo buildInfo)77     public void setBuild(IBuildInfo buildInfo) {
78         mCtsBuild = buildInfo;
79     }
80 
saveFeature(final String feature)81     private void saveFeature(final String feature) throws Exception {
82         mDefaultValue = DeviceUtils.getDeviceConfigFeature(getDevice(), FEATURE_NAMESPACE,
83                 feature);
84     }
85 
setFeature(final String feature, final String enable)86     private void setFeature(final String feature, final String enable) throws Exception {
87         DeviceUtils.putDeviceConfigFeature(getDevice(), FEATURE_NAMESPACE, feature, enable);
88     }
89 
restoreFeature(String feature)90     private void restoreFeature(String feature) throws Exception {
91         if (mDefaultValue == null || mDefaultValue.equals("null")) {
92             DeviceUtils.deleteDeviceConfigFeature(getDevice(), FEATURE_NAMESPACE, feature);
93         } else {
94             DeviceUtils.putDeviceConfigFeature(getDevice(), FEATURE_NAMESPACE, feature,
95                     mDefaultValue);
96         }
97     }
98     /**
99      * Test High Perf lock with the wifi:high_perf_lock_deprecated = false.
100      */
testWifiLockHighPerf()101     public void testWifiLockHighPerf() throws Exception {
102         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
103         if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) return;
104 
105         setFeature("high_perf_lock_deprecated", "false");
106         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
107                 AtomsProto.Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER, true);
108         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiLockHighPerf");
109         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
110 
111         // Sorted list of events in order in which they occurred.
112         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
113 
114         Set<Integer> lockOn = new HashSet<>(
115                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.ON_VALUE));
116         Set<Integer> lockOff = new HashSet<>(
117                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.OFF_VALUE));
118 
119         // Add state sets to the list in order.
120         List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
121 
122         // Assert that the events happened in the expected order.
123         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
124                 atom -> atom.getWifiLockStateChanged().getState().getNumber());
125 
126         for (StatsLog.EventMetricData event : data) {
127             assertThat(event.getAtom().getWifiLockStateChanged().getMode())
128                     .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
129         }
130     }
131 
132     /**
133      * Test High Perf lock with the wifi:high_perf_lock_deprecated = true.
134      */
testWifiLockHighPerfDeprecated()135     public void testWifiLockHighPerfDeprecated() throws Exception {
136         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
137         if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) return;
138 
139         setFeature("high_perf_lock_deprecated", "true");
140         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
141                 AtomsProto.Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER, true);
142 
143         // High perf lock is deprecated from Android U onwards. Acquisition of  High perf lock will
144         // be treated as a call to Low Latency Lock.
145         //
146         // For low latency lock to be active following conditions need to be met,
147         //    - Wi-Fi is connected
148         //    - Screen On
149         //    - Application is foreground.
150 
151         // Check Wi-Fi is connected.
152         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiConnected");
153 
154         // Turn screen on.
155         DeviceUtils.turnScreenOn(getDevice());
156 
157         // Acquire and release highperf lock in foreground activity.
158         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
159                 "StatsdCtsForegroundActivity", "action", "action.acquire_release_wifi_hiperf_lock");
160         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
161 
162         // Sorted list of events in order in which they occurred.
163         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
164 
165         Set<Integer> lockOn = new HashSet<>(
166                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.ON_VALUE));
167         Set<Integer> lockOff = new HashSet<>(
168                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.OFF_VALUE));
169 
170         // Add state sets to the list in order.
171         List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
172 
173         // Assert that the events happened in the expected order.
174         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
175                 atom -> atom.getWifiLockStateChanged().getState().getNumber());
176 
177         for (StatsLog.EventMetricData event : data) {
178             // High perf lock is deprecated from Android U onwards. Acquisition of high perf lock is
179             // treated as low latency lock.
180             assertThat(event.getAtom().getWifiLockStateChanged().getMode())
181                     .isEqualTo(
182                             (ApiLevelUtil.isAfter(getDevice(), "TIRAMISU")
183                                     || ApiLevelUtil.codenameStartsWith(getDevice(), "U"))
184                                     ? WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY
185                                     : WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
186         }
187     }
188 
testWifiLockLowLatency()189     public void testWifiLockLowLatency() throws Exception {
190         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
191         if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) return;
192 
193         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
194                 AtomsProto.Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER, true);
195 
196         // For low latency lock to be active following conditions need to be met,
197         //    - Wi-Fi is connected
198         //    - Screen On
199         //    - Application is foreground.
200 
201         // Check Wi-Fi is connected.
202         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiConnected");
203 
204         // Turn screen on.
205         DeviceUtils.turnScreenOn(getDevice());
206 
207         // Acquire and release low latency lock in foreground activity.
208         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
209                 "StatsdCtsForegroundActivity", "action", "action.acquire_release_wifi_ll_lock");
210         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
211 
212         // Sorted list of events in order in which they occurred.
213         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
214 
215         Set<Integer> lockOn = new HashSet<>(
216                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.ON_VALUE));
217         Set<Integer> lockOff = new HashSet<>(
218                 Collections.singletonList(AtomsProto.WifiLockStateChanged.State.OFF_VALUE));
219 
220         // Add state sets to the list in order.
221         List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
222 
223         // Assert that the events happened in the expected order.
224         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
225                 atom -> atom.getWifiLockStateChanged().getState().getNumber());
226 
227         for (StatsLog.EventMetricData event : data) {
228             assertThat(event.getAtom().getWifiLockStateChanged().getMode())
229                     .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
230         }
231     }
232 
testWifiMulticastLock()233     public void testWifiMulticastLock() throws Exception {
234         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
235         if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) return;
236 
237         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
238                 AtomsProto.Atom.WIFI_MULTICAST_LOCK_STATE_CHANGED_FIELD_NUMBER, true);
239         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiMulticastLock");
240         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
241 
242         // Sorted list of events in order in which they occurred.
243         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
244 
245         Set<Integer> lockOn = new HashSet<>(
246                 Collections.singletonList(AtomsProto.WifiMulticastLockStateChanged.State.ON_VALUE));
247         Set<Integer> lockOff = new HashSet<>(
248                 Collections.singletonList(
249                         AtomsProto.WifiMulticastLockStateChanged.State.OFF_VALUE));
250 
251         final String EXPECTED_TAG = "StatsdCTSMulticastLock";
252 
253         // Add state sets to the list in order.
254         List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
255 
256         // Assert that the events happened in the expected order.
257         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
258                 atom -> atom.getWifiMulticastLockStateChanged().getState().getNumber());
259 
260         for (StatsLog.EventMetricData event : data) {
261             String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
262             assertThat(tag).isEqualTo(EXPECTED_TAG);
263         }
264     }
265 
testWifiReconnect()266     public void testWifiReconnect() throws Exception {
267         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
268 
269         ConfigUtils.uploadConfigForPushedAtoms(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
270                 new int[] {
271                         AtomsProto.Atom.WIFI_CONNECTION_RESULT_REPORTED_FIELD_NUMBER,
272                         AtomsProto.Atom.WIFI_DISCONNECT_REPORTED_FIELD_NUMBER
273                 });
274 
275         // This test on device checks if device is connected, and connects it if it is not;
276         // Afterwards, it disconnects from that network and connects back to it.
277         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiReconnect");
278         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
279 
280         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
281 
282         // If device had Wifi connected, we'll see two atoms: disconnect, connect.
283         // If it was not connected, we'll see three: connect, disconnect, connect.
284         // We're only interested in the disconnect-connect pair.
285         assertWithMessage(
286                 "Expected disconnected and connected atoms, got: \n" +
287                         data.stream().map(AbstractMessage::toString).reduce((acc, i) -> acc + i)
288         ).that(data.size()).isIn(Range.closed(2, 3));
289 
290         AtomsProto.WifiDisconnectReported a0 =
291                 data.get(data.size() - 2).getAtom().getWifiDisconnectReported();
292         AtomsProto.WifiConnectionResultReported a1 =
293                 data.get(data.size() - 1).getAtom().getWifiConnectionResultReported();
294 
295         assertThat(a0).isNotNull();
296         assertThat(a1).isNotNull();
297 
298         assertThat(a0.getConnectedDurationSeconds()).isGreaterThan(0);
299         int maxLinkSpeedMbps = 1_000_000; /* 640K ought to be enough for anybody. */
300         assertThat(a0.getLastLinkSpeed()).isIn(Range.open(0, maxLinkSpeedMbps));
301         assertThat(a0.getLastRssi()).isIn(Range.closed(-127, 0));
302 
303         assertThat(a1.getConnectionResult()).isTrue();
304         assertThat(a1.getRssi()).isIn(Range.closed(-127, 0));
305         assertThat(a1.getConnectionAttemptDurationMillis()).isIn(
306                 Range.open(0, WIFI_CONNECT_TIMEOUT_MILLIS));
307         assertThat(a1.getTrigger()).isEqualTo(
308                 AtomsProto.WifiConnectionResultReported.Trigger.RECONNECT_SAME_NETWORK);
309         assertThat(a1.getNetworkUsed()).isTrue();
310     }
311 
testWifiScanLogsScanAtoms()312     public void testWifiScanLogsScanAtoms() throws Exception {
313         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
314 
315         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
316                 AtomsProto.Atom.WIFI_SCAN_REPORTED_FIELD_NUMBER);
317         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiScan");
318         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
319 
320         List<StatsLog.EventMetricData> metricData = ReportUtils.getEventMetricDataList(getDevice());
321         List<AtomsProto.WifiScanReported> wifiScanAtoms = metricData.stream()
322                 .map(eventLog -> eventLog.getAtom().getWifiScanReported())
323                 // Disregard interfering scans from other sources.
324                 // If this test is run on a device that has a Settings app open that
325                 // continuously performs frequent scans, quite often our scans requests
326                 // are bundled together and get attributed to the Settings app.
327                 .filter(scan -> List.of(
328                                 AtomsProto.WifiScanReported.Source.SOURCE_OTHER_APP,
329                                 AtomsProto.WifiScanReported.Source.SOURCE_SETTINGS_APP)
330                         .contains(scan.getSource()))
331                 .filter(Predicate.not(scan -> scan.getImportance().equals(
332                         AtomsProto.WifiScanReported.Importance.IMPORTANCE_UNKNOWN)))
333                 .collect(Collectors.toList());
334         assertThat(wifiScanAtoms).isNotEmpty();
335 
336         for (AtomsProto.WifiScanReported scan : wifiScanAtoms) {
337             assertThat(scan.getResult()).isEqualTo(
338                     AtomsProto.WifiScanReported.Result.RESULT_SUCCESS);
339             assertThat(scan.getType()).isEqualTo(AtomsProto.WifiScanReported.Type.TYPE_SINGLE);
340             List<AtomsProto.WifiScanReported.Importance> expectedResult =
341                     Arrays.asList(
342                             AtomsProto.WifiScanReported.Importance.IMPORTANCE_FOREGROUND_SERVICE,
343                             AtomsProto.WifiScanReported.Importance.IMPORTANCE_FOREGROUND);
344             assertThat(scan.getImportance()).isIn(expectedResult);
345             assertThat(scan.getScanDurationMillis()).isGreaterThan(0);
346         }
347     }
348 
testWifiScanLogsStateChangedAtoms()349     public void testWifiScanLogsStateChangedAtoms() throws Exception {
350         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
351 
352 
353         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
354                 AtomsProto.Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER, true);
355         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiScan");
356         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
357 
358         final int stateOn = AtomsProto.WifiScanStateChanged.State.ON_VALUE;
359         final int stateOff = AtomsProto.WifiScanStateChanged.State.OFF_VALUE;
360         final int minTimeDiffMillis = 250;
361         final int maxTimeDiffMillis = 60_000;
362 
363         List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
364         assertThat(data.size()).isIn(Range.closed(2, 4));
365         AtomTestUtils.assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMillis,
366                 maxTimeDiffMillis);
367         AtomsProto.WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
368         AtomsProto.WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
369         assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
370         assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
371     }
372 }
373