Merge changes Id928678b,Ic55dbbe6 into rvc-dev

* changes:
  ConnectedNetworkScorerTest: Add test for connected network scorer
  ConnectedNetworkScorerTest: Add tests for wifi usability stats
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 0000000..ce5bb81
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
@@ -0,0 +1,344 @@
+/*
+ * 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);
+        assertThat(mWifiManager).isNotNull();
+
+        // 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<WifiConfiguration> 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();
+        }
+    }
+
+    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();
+        }
+    }
+}