From 5dd8299525eb59c205f01bec336148bd57b5daec Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Thu, 19 Mar 2020 14:24:05 -0700 Subject: [PATCH 1/2] ConnectedNetworkScorerTest: Add tests for wifi usability stats Bug: 150236894 Bug: 150973472 Test: atest android.net.wifi.cts.ConnectedNetworkScorerTest Change-Id: Ic55dbbe672da5492db5382aa6c427976355518ed --- .../wifi/cts/ConnectedNetworkScorerTest.java | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java diff --git a/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java b/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java new file mode 100644 index 0000000000..5869ee8ea4 --- /dev/null +++ b/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.cts; + +import static android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_FAILURE; +import static android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_NO_PROBE; +import static android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_SUCCESS; +import static android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_UNKNOWN; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.UiAutomation; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiUsabilityStatsEntry; +import android.support.test.uiautomator.UiDevice; +import android.telephony.TelephonyManager; +import android.test.AndroidTestCase; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.PollingCheck; +import com.android.compatibility.common.util.ShellIdentityUtils; +import com.android.compatibility.common.util.SystemUtil; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class ConnectedNetworkScorerTest extends AndroidTestCase { + private WifiManager mWifiManager; + private UiDevice mUiDevice; + private boolean mWasVerboseLoggingEnabled; + + private static final int DURATION = 10_000; + private static final int DURATION_SCREEN_TOGGLE = 2000; + + @Override + protected void setUp() throws Exception { + super.setUp(); + if (!WifiFeature.isWifiSupported(getContext())) { + // skip the test if WiFi is not supported + return; + } + mWifiManager = getContext().getSystemService(WifiManager.class); + assertNotNull(mWifiManager); + + // turn on verbose logging for tests + mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( + () -> mWifiManager.isVerboseLoggingEnabled()); + ShellIdentityUtils.invokeWithShellPermissions( + () -> mWifiManager.setVerboseLoggingEnabled(true)); + + if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true); + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + turnScreenOn(); + PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled()); + List savedNetworks = ShellIdentityUtils.invokeWithShellPermissions( + () -> mWifiManager.getConfiguredNetworks()); + assertFalse("Need at least one saved network", savedNetworks.isEmpty()); + // Wait for wifi is to be connected + PollingCheck.check( + "Wifi not connected", + DURATION, + () -> mWifiManager.getConnectionInfo().getNetworkId() != -1); + assertThat(mWifiManager.getConnectionInfo().getNetworkId()).isNotEqualTo(-1); + } + + @Override + protected void tearDown() throws Exception { + if (!WifiFeature.isWifiSupported(getContext())) { + // skip the test if WiFi is not supported + super.tearDown(); + return; + } + if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true); + turnScreenOff(); + ShellIdentityUtils.invokeWithShellPermissions( + () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled)); + super.tearDown(); + } + + private void setWifiEnabled(boolean enable) throws Exception { + // now trigger the change using shell commands. + SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable")); + } + + private void turnScreenOn() throws Exception { + mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); + mUiDevice.executeShellCommand("wm dismiss-keyguard"); + // Since the screen on/off intent is ordered, they will not be sent right now. + Thread.sleep(DURATION_SCREEN_TOGGLE); + } + + private void turnScreenOff() throws Exception { + mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP"); + } + + private static class TestUsabilityStatsListener implements + WifiManager.OnWifiUsabilityStatsListener { + private final CountDownLatch mCountDownLatch; + public int seqNum; + public boolean isSameBssidAndFre; + public WifiUsabilityStatsEntry statsEntry; + + TestUsabilityStatsListener(CountDownLatch countDownLatch) { + mCountDownLatch = countDownLatch; + } + + @Override + public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq, + WifiUsabilityStatsEntry statsEntry) { + this.seqNum = seqNum; + this.isSameBssidAndFre = isSameBssidAndFreq; + this.statsEntry = statsEntry; + mCountDownLatch.countDown(); + } + } + + /** + * Tests the {@link android.net.wifi.WifiUsabilityStatsEntry} retrieved from + * {@link WifiManager.OnWifiUsabilityStatsListener}. + */ + public void testWifiUsabilityStatsEntry() throws Exception { + if (!WifiFeature.isWifiSupported(getContext())) { + // skip the test if WiFi is not supported + return; + } + CountDownLatch countDownLatch = new CountDownLatch(1); + UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + TestUsabilityStatsListener usabilityStatsListener = + new TestUsabilityStatsListener(countDownLatch); + try { + uiAutomation.adoptShellPermissionIdentity(); + mWifiManager.addOnWifiUsabilityStatsListener( + Executors.newSingleThreadExecutor(), usabilityStatsListener); + // Wait for new usability stats (while connected & screen on this is triggered + // by platform periodically). + assertThat(countDownLatch.await(DURATION, TimeUnit.MILLISECONDS)).isTrue(); + + assertThat(usabilityStatsListener.statsEntry).isNotNull(); + WifiUsabilityStatsEntry statsEntry = usabilityStatsListener.statsEntry; + + assertThat(statsEntry.getTimeStampMillis()).isGreaterThan(0L); + assertThat(statsEntry.getRssi()).isLessThan(0); + assertThat(statsEntry.getLinkSpeedMbps()).isGreaterThan(0); + assertThat(statsEntry.getTotalTxSuccess()).isGreaterThan(0L); + assertThat(statsEntry.getTotalTxRetries()).isAtLeast(0L); + assertThat(statsEntry.getTotalTxBad()).isAtLeast(0L); + assertThat(statsEntry.getTotalRxSuccess()).isAtLeast(0L); + assertThat(statsEntry.getTotalRadioOnTimeMillis()).isGreaterThan(0L); + assertThat(statsEntry.getTotalRadioTxTimeMillis()).isGreaterThan(0L); + assertThat(statsEntry.getTotalRadioRxTimeMillis()).isGreaterThan(0L); + assertThat(statsEntry.getTotalScanTimeMillis()).isGreaterThan(0L); + assertThat(statsEntry.getTotalNanScanTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalBackgroundScanTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalRoamScanTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalPnoScanTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalHotspot2ScanTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalCcaBusyFreqTimeMillis()).isAtLeast(0L); + assertThat(statsEntry.getTotalRadioOnTimeMillis()).isGreaterThan(0L); + assertThat(statsEntry.getTotalBeaconRx()).isGreaterThan(0L); + assertThat(statsEntry.getProbeStatusSinceLastUpdate()) + .isAnyOf(PROBE_STATUS_SUCCESS, + PROBE_STATUS_FAILURE, + PROBE_STATUS_NO_PROBE, + PROBE_STATUS_UNKNOWN); + // -1 is default value for some of these fields if they're not available. + assertThat(statsEntry.getProbeElapsedTimeSinceLastUpdateMillis()).isAtLeast(-1); + assertThat(statsEntry.getProbeMcsRateSinceLastUpdate()).isAtLeast(-1); + assertThat(statsEntry.getRxLinkSpeedMbps()).isAtLeast(-1); + // no longer populated, return default value. + assertThat(statsEntry.getCellularDataNetworkType()) + .isAnyOf(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyManager.NETWORK_TYPE_GPRS, + TelephonyManager.NETWORK_TYPE_EDGE, + TelephonyManager.NETWORK_TYPE_UMTS, + TelephonyManager.NETWORK_TYPE_CDMA, + TelephonyManager.NETWORK_TYPE_EVDO_0, + TelephonyManager.NETWORK_TYPE_EVDO_A, + TelephonyManager.NETWORK_TYPE_1xRTT, + TelephonyManager.NETWORK_TYPE_HSDPA, + TelephonyManager.NETWORK_TYPE_HSUPA, + TelephonyManager.NETWORK_TYPE_HSPA, + TelephonyManager.NETWORK_TYPE_IDEN, + TelephonyManager.NETWORK_TYPE_EVDO_B, + TelephonyManager.NETWORK_TYPE_LTE, + TelephonyManager.NETWORK_TYPE_EHRPD, + TelephonyManager.NETWORK_TYPE_HSPAP, + TelephonyManager.NETWORK_TYPE_GSM, + TelephonyManager.NETWORK_TYPE_TD_SCDMA, + TelephonyManager.NETWORK_TYPE_IWLAN, + TelephonyManager.NETWORK_TYPE_NR); + assertThat(statsEntry.getCellularSignalStrengthDbm()).isAtMost(0); + assertThat(statsEntry.getCellularSignalStrengthDb()).isAtMost(0); + assertThat(statsEntry.isSameRegisteredCell()).isFalse(); + } finally { + mWifiManager.removeOnWifiUsabilityStatsListener(usabilityStatsListener); + uiAutomation.dropShellPermissionIdentity(); + } + } + + /** + * Tests the {@link android.net.wifi.WifiManager#updateWifiUsabilityScore(int, int, int)} + */ + public void testUpdateWifiUsabilityScore() throws Exception { + if (!WifiFeature.isWifiSupported(getContext())) { + // skip the test if WiFi is not supported + return; + } + UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + try { + uiAutomation.adoptShellPermissionIdentity(); + // update scoring with dummy values. + mWifiManager.updateWifiUsabilityScore(0, 50, 50); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } + } + +} From b6b4c49895b700081e021c5b80fbd89748487bd2 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Thu, 19 Mar 2020 15:09:28 -0700 Subject: [PATCH 2/2] ConnectedNetworkScorerTest: Add test for connected network scorer Bug: 150236894 Bug: 150973472 Test: atest android.net.wifi.cts.ConnectedNetworkScorerTest Change-Id: Id928678bfefe00f66229f4dbd9915c7f41274972 --- .../wifi/cts/ConnectedNetworkScorerTest.java | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java b/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java index 5869ee8ea4..ce5bb81d7b 100644 --- a/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java +++ b/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java @@ -58,7 +58,7 @@ public class ConnectedNetworkScorerTest extends AndroidTestCase { return; } mWifiManager = getContext().getSystemService(WifiManager.class); - assertNotNull(mWifiManager); + assertThat(mWifiManager).isNotNull(); // turn on verbose logging for tests mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( @@ -233,4 +233,112 @@ public class ConnectedNetworkScorerTest extends AndroidTestCase { } } + private static class TestConnectedNetworkScorer implements + WifiManager.WifiConnectedNetworkScorer { + private CountDownLatch mCountDownLatch; + public int startSessionId; + public int stopSessionId; + public WifiManager.ScoreUpdateObserver scoreUpdateObserver; + + TestConnectedNetworkScorer(CountDownLatch countDownLatch) { + mCountDownLatch = countDownLatch; + } + + @Override + public void onStart(int sessionId) { + synchronized (mCountDownLatch) { + this.startSessionId = sessionId; + mCountDownLatch.countDown(); + } + } + + @Override + public void onStop(int sessionId) { + synchronized (mCountDownLatch) { + this.stopSessionId = sessionId; + mCountDownLatch.countDown(); + } + } + + @Override + public void onSetScoreUpdateObserver(WifiManager.ScoreUpdateObserver observerImpl) { + this.scoreUpdateObserver = observerImpl; + } + + public void resetCountDownLatch(CountDownLatch countDownLatch) { + synchronized (mCountDownLatch) { + mCountDownLatch = countDownLatch; + } + } + } + + /** + * Tests the {@link android.net.wifi.WifiConnectedNetworkScorer} interface. + * + * Note: We could write more interesting test cases (if the device has a mobile connection), but + * that would make the test flaky. The default network/route selection on the device is not just + * controlled by the wifi scorer input, but also based on params which are controlled by + * other parts of the platform (likely in connectivity service) and hence will behave + * differently on OEM devices. + */ + public void testSetWifiConnectedNetworkScorer() throws Exception { + if (!WifiFeature.isWifiSupported(getContext())) { + // skip the test if WiFi is not supported + return; + } + CountDownLatch countDownLatchScorer = new CountDownLatch(1); + CountDownLatch countDownLatchUsabilityStats = new CountDownLatch(1); + UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + TestConnectedNetworkScorer connectedNetworkScorer = + new TestConnectedNetworkScorer(countDownLatchScorer); + TestUsabilityStatsListener usabilityStatsListener = + new TestUsabilityStatsListener(countDownLatchUsabilityStats); + try { + uiAutomation.adoptShellPermissionIdentity(); + mWifiManager.setWifiConnectedNetworkScorer( + Executors.newSingleThreadExecutor(), connectedNetworkScorer); + // Since we're already connected, wait for onStart to be invoked. + assertThat(countDownLatchScorer.await(DURATION, TimeUnit.MILLISECONDS)).isTrue(); + + assertThat(connectedNetworkScorer.startSessionId).isAtLeast(0); + assertThat(connectedNetworkScorer.scoreUpdateObserver).isNotNull(); + WifiManager.ScoreUpdateObserver scoreUpdateObserver = + connectedNetworkScorer.scoreUpdateObserver; + + // Now trigger a dummy score update. + scoreUpdateObserver.notifyScoreUpdate(connectedNetworkScorer.startSessionId, 50); + + // Register the usability listener + mWifiManager.addOnWifiUsabilityStatsListener( + Executors.newSingleThreadExecutor(), usabilityStatsListener); + // Trigger a usability stats update. + scoreUpdateObserver.triggerUpdateOfWifiUsabilityStats( + connectedNetworkScorer.startSessionId); + // Ensure that we got the stats update callback. + assertThat(countDownLatchUsabilityStats.await(DURATION, TimeUnit.MILLISECONDS)) + .isTrue(); + assertThat(usabilityStatsListener.seqNum).isAtLeast(0); + + // Reset the scorer countdown latch for onStop + countDownLatchScorer = new CountDownLatch(1); + connectedNetworkScorer.resetCountDownLatch(countDownLatchScorer); + // Now disconnect from the network. + mWifiManager.disconnect(); + // Wait for it to be disconnected. + PollingCheck.check( + "Wifi not disconnected", + DURATION, + () -> mWifiManager.getConnectionInfo().getNetworkId() == -1); + assertThat(mWifiManager.getConnectionInfo().getNetworkId()).isEqualTo(-1); + + // Wait for stop to be invoked and ensure that the session id matches. + assertThat(countDownLatchScorer.await(DURATION, TimeUnit.MILLISECONDS)).isTrue(); + assertThat(connectedNetworkScorer.stopSessionId) + .isEqualTo(connectedNetworkScorer.startSessionId); + } finally { + mWifiManager.removeOnWifiUsabilityStatsListener(usabilityStatsListener); + mWifiManager.clearWifiConnectedNetworkScorer(); + uiAutomation.dropShellPermissionIdentity(); + } + } }