diff options
author | Michael Plass <mplass@google.com> | 2020-04-10 08:37:19 -0700 |
---|---|---|
committer | Michael Plass <mplass@google.com> | 2020-04-20 14:36:53 +0000 |
commit | 9d1fa2cdb82d7435d25d88854726528962ca79c9 (patch) | |
tree | 3371e1c6d664ef9dbd81acb9388bd83a7e052ee4 | |
parent | 1c93299eed327259785fd3a747d8abdec08397eb (diff) |
Estimate the probability of getting internet
Use WifiScoreCard statistics to estimate how likely it is that
a candidate BSSID will be able to provide internet access.
Also hook up the success/failure stats for roaming.
Bug: 111230798
Test: atest WifiScoreCardTest WifiCandidatesTest
Change-Id: Ic344ce78c1544fb0f9be46a40e3900067cf5d991
7 files changed, 163 insertions, 17 deletions
diff --git a/service/java/com/android/server/wifi/WifiCandidates.java b/service/java/com/android/server/wifi/WifiCandidates.java index 24f814023..0513e5c1e 100644 --- a/service/java/com/android/server/wifi/WifiCandidates.java +++ b/service/java/com/android/server/wifi/WifiCandidates.java @@ -141,10 +141,14 @@ public class WifiCandidates { */ int getFrequency(); /** - * Gets the predicted throughput in Mbps + * Gets the predicted throughput in Mbps. */ int getPredictedThroughputMbps(); /** + * Estimated probability of getting internet access (percent 0-100). + */ + int getEstimatedPercentInternetAvailability(); + /** * Gets statistics from the scorecard. */ @Nullable WifiScoreCardProto.Signal getEventStatistics(WifiScoreCardProto.Event event); @@ -171,6 +175,7 @@ public class WifiCandidates { private final boolean mTrusted; private final boolean mCarrierOrPrivileged; private final int mPredictedThroughputMbps; + private final int mEstimatedPercentInternetAvailability; CandidateImpl(Key key, WifiConfiguration config, WifiScoreCard.PerBssid perBssid, @@ -200,6 +205,8 @@ public class WifiCandidates { this.mTrusted = config.trusted; this.mCarrierOrPrivileged = isCarrierOrPrivileged; this.mPredictedThroughputMbps = predictedThroughputMbps; + this.mEstimatedPercentInternetAvailability = perBssid == null ? 50 : + perBssid.estimatePercentInternetAvailability(); } @Override @@ -287,6 +294,11 @@ public class WifiCandidates { return mPredictedThroughputMbps; } + @Override + public int getEstimatedPercentInternetAvailability() { + return mEstimatedPercentInternetAvailability; + } + /** * Accesses statistical information from the score card */ @@ -309,12 +321,13 @@ public class WifiCandidates { + ", "; } return "Candidate { " - + "networkId = " + getNetworkConfigId() + ", " + + "config = " + getNetworkConfigId() + ", " + "bssid = " + key.bssid + ", " - + "frequency = " + getFrequency() + ", " + + "freq = " + getFrequency() + ", " + "rssi = " + getScanRssi() + ", " + "Mbps = " + getPredictedThroughputMbps() + ", " + "nominator = " + getNominatorId() + ", " + + "pInternet = " + getEstimatedPercentInternetAvailability() + ", " + lastSelectionWeightString + (isCurrentBssid() ? "connected, " : "") + (isCurrentNetwork() ? "current, " : "") diff --git a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java index 6bb3708ac..6bb3708ac 100755..100644 --- a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java +++ b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java index fdf7a678b..071b9634d 100644 --- a/service/java/com/android/server/wifi/WifiScoreCard.java +++ b/service/java/com/android/server/wifi/WifiScoreCard.java @@ -205,7 +205,7 @@ public class WifiScoreCard { * * We want to gather statistics only on the first success. */ - private boolean mValidated = false; + private boolean mValidatedThisConnectionAtLeastOnce = false; /** * A note to ourself that we are attempting a network switch @@ -279,7 +279,7 @@ public class WifiScoreCard { } mTsRoam = TS_NONE; mPolled = false; - mValidated = false; + mValidatedThisConnectionAtLeastOnce = false; mNonlocalDisconnection = false; } @@ -363,9 +363,9 @@ public class WifiScoreCard { * @param wifiInfo object holding relevant values */ public void noteValidationSuccess(@NonNull ExtendedWifiInfo wifiInfo) { - if (mValidated) return; // Only once per connection + if (mValidatedThisConnectionAtLeastOnce) return; // Only once per connection updatePerBssid(Event.VALIDATION_SUCCESS, wifiInfo); - mValidated = true; + mValidatedThisConnectionAtLeastOnce = true; doWrites(); } @@ -375,7 +375,7 @@ public class WifiScoreCard { * @param wifiInfo object holding relevant values */ public void noteValidationFailure(@NonNull ExtendedWifiInfo wifiInfo) { - mValidated = false; + // VALIDATION_FAILURE is not currently recorded. } /** @@ -448,10 +448,11 @@ public class WifiScoreCard { * @param wifiInfo object holding relevant values */ public void noteIpReachabilityLost(@NonNull ExtendedWifiInfo wifiInfo) { - updatePerBssid(Event.IP_REACHABILITY_LOST, wifiInfo); if (mTsRoam > TS_NONE) { mTsConnectionAttemptStart = mTsRoam; // just to update elapsed updatePerBssid(Event.ROAM_FAILURE, wifiInfo); + } else { + updatePerBssid(Event.IP_REACHABILITY_LOST, wifiInfo); } // No need to call resetConnectionStateInternal() because // resetConnectionState() will be called after WifiNative.disconnect() in ClientModeImpl @@ -466,7 +467,7 @@ public class WifiScoreCard { * * @param wifiInfo object holding relevant values */ - public void noteRoam(@NonNull ExtendedWifiInfo wifiInfo) { + private void noteRoam(@NonNull ExtendedWifiInfo wifiInfo) { updatePerBssid(Event.LAST_POLL_BEFORE_ROAM, wifiInfo); mTsRoam = mClock.getElapsedSinceBootMillis(); } @@ -479,6 +480,10 @@ public class WifiScoreCard { */ public void noteSupplicantStateChanging(@NonNull ExtendedWifiInfo wifiInfo, SupplicantState state) { + if (state == SupplicantState.COMPLETED && wifiInfo.getSupplicantState() == state) { + // Our signal that a firmware roam has occurred + noteRoam(wifiInfo); + } logd("Changing state to " + state + " " + wifiInfo); } @@ -788,6 +793,38 @@ public class WifiScoreCard { } merge(ap); } + + /** + * Estimates the probability of getting internet access, based on the + * device experience. + * + * @return a probability, expressed as a percentage in the range 0 to 100 + */ + public int estimatePercentInternetAvailability() { + // Initialize counts accoring to Laplace's rule of succession + int trials = 2; + int successes = 1; + // Aggregate over all of the frequencies + for (PerSignal s : mSignalForEventAndFrequency.values()) { + switch (s.event) { + case IP_CONFIGURATION_SUCCESS: + if (s.elapsedMs != null) { + trials += s.elapsedMs.count; + } + break; + case VALIDATION_SUCCESS: + if (s.elapsedMs != null) { + successes += s.elapsedMs.count; + } + break; + default: + break; + } + } + // Note that because of roaming it is possible to count successes + // without corresponding trials. + return Math.min(Math.max(Math.round(successes * 100.0f / trials), 0), 100); + } } /** @@ -1712,7 +1749,7 @@ public class WifiScoreCard { Preconditions.checkArgument(frequency == signal.getFrequency()); rssi.merge(signal.getRssi()); linkspeed.merge(signal.getLinkspeed()); - if (signal.hasElapsedMs()) { + if (elapsedMs != null && signal.hasElapsedMs()) { elapsedMs.merge(signal.getElapsedMs()); } return this; diff --git a/tests/wifitests/Android.bp b/tests/wifitests/Android.bp index 6ba8c908a..851a6016e 100644 --- a/tests/wifitests/Android.bp +++ b/tests/wifitests/Android.bp @@ -402,6 +402,9 @@ android_test { "com.android.server.wifi.WifiCandidates", "com.android.server.wifi.WifiCandidates$*", "com.android.server.wifi.WifiCandidates.**", + "com.android.server.wifi.WifiCarrierInfoManager", + "com.android.server.wifi.WifiCarrierInfoManager$*", + "com.android.server.wifi.WifiCarrierInfoManager.**", "com.android.server.wifi.WifiChannelUtilization", "com.android.server.wifi.WifiChannelUtilization$*", "com.android.server.wifi.WifiChannelUtilization.**", @@ -933,9 +936,6 @@ android_test { "com.android.server.wifi.util.StringUtil", "com.android.server.wifi.util.StringUtil$*", "com.android.server.wifi.util.StringUtil.**", - "com.android.server.wifi.WifiCarrierInfoManager", - "com.android.server.wifi.WifiCarrierInfoManager$*", - "com.android.server.wifi.WifiCarrierInfoManager.**", "com.android.server.wifi.util.TimedQuotaManager", "com.android.server.wifi.util.TimedQuotaManager$*", "com.android.server.wifi.util.TimedQuotaManager.**", diff --git a/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java b/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java index b95a1171b..37d2945cf 100644 --- a/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java +++ b/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java @@ -40,6 +40,7 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { private int mScanRssi = -127; private int mFrequency = -1; private int mPredictedThroughputMbps = 0; + private int mEstimatedPercentInternetAvailability = 50; private final Map<WifiScoreCardProto.Event, WifiScoreCardProto.Signal> mEventStatisticsMap = new ArrayMap<>(); @@ -65,6 +66,8 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { mScanRssi = candidate.getScanRssi(); mFrequency = candidate.getFrequency(); mPredictedThroughputMbps = candidate.getPredictedThroughputMbps(); + mEstimatedPercentInternetAvailability = candidate + .getEstimatedPercentInternetAvailability(); for (WifiScoreCardProto.Event event : WifiScoreCardProto.Event.values()) { WifiScoreCardProto.Signal signal = candidate.getEventStatistics(event); if (signal != null) { @@ -243,6 +246,16 @@ public final class ConcreteCandidate implements WifiCandidates.Candidate { return mPredictedThroughputMbps; } + public ConcreteCandidate setEstimatedPercentInternetAvailability(int percent) { + mEstimatedPercentInternetAvailability = percent; + return this; + } + + @Override + public int getEstimatedPercentInternetAvailability() { + return mEstimatedPercentInternetAvailability; + } + public ConcreteCandidate setEventStatistics( WifiScoreCardProto.Event event, WifiScoreCardProto.Signal signal) { diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java index 7ce59c9ee..9ff064ed1 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java @@ -79,6 +79,7 @@ public class WifiCandidatesTest extends WifiBaseTest { doReturn(mScanResult1).when(mScanDetail1).getScanResult(); doReturn(mScanResult2).when(mScanDetail2).getScanResult(); doReturn(mPerBssid).when(mWifiScoreCard).lookupBssid(any(), any()); + doReturn(50).when(mPerBssid).estimatePercentInternetAvailability(); MockResources mResources = new MockResources(); mResources.setBoolean(R.bool.config_wifiSaeUpgradeEnabled, true); doReturn(mResources).when(mContext).getResources(); @@ -182,13 +183,15 @@ public class WifiCandidatesTest extends WifiBaseTest { */ @Test public void testCandidateToString() throws Exception { + doReturn(57).when(mPerBssid).estimatePercentInternetAvailability(); mWifiCandidates.add(mScanDetail1, mConfig1, 2, 0.0015001, false, 100); WifiCandidates.Candidate c = mWifiCandidates.getGroupedCandidates() .iterator().next().iterator().next(); String s = c.toString(); assertTrue(s, s.contains(" nominator = 2, ")); - assertTrue(s, s.contains(" networkId = " + mConfig1.networkId + ", ")); + assertTrue(s, s.contains(" config = " + mConfig1.networkId + ", ")); assertTrue(s, s.contains(" lastSelectionWeight = 0.002, ")); // should be rounded + assertTrue(s, s.contains(" pInternet = 57, ")); for (String x : s.split(",")) { if (x.startsWith("Candidate {")) x = x.substring("Candidate {".length()); if (x.endsWith(" }")) x = x.substring(0, x.length() - 2); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java index 3fc62609a..e5525c4c7 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.*; import android.content.Context; import android.net.MacAddress; +import android.net.wifi.SupplicantState; import android.net.wifi.WifiInfo; import android.net.wifi.WifiSsid; import android.util.Base64; @@ -72,6 +73,7 @@ import java.util.List; */ @SmallTest public class WifiScoreCardTest extends WifiBaseTest { + static final WifiSsid TEST_SSID_1 = WifiSsid.createFromAsciiEncoded("Joe's Place"); static final WifiSsid TEST_SSID_2 = WifiSsid.createFromAsciiEncoded("Poe's Ravn"); @@ -364,9 +366,10 @@ public class WifiScoreCardTest extends WifiBaseTest { secondsPass(9900); // Second validation success should not matter. mWifiScoreCard.noteValidationSuccess(mWifiInfo); + mWifiInfo.setRssi(-88); + mWifiScoreCard.noteIpReachabilityLost(mWifiInfo); mWifiScoreCard.noteWifiDisabled(mWifiInfo); - // Now verify WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1); assertEquals(1, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805) @@ -375,14 +378,91 @@ public class WifiScoreCardTest extends WifiBaseTest { .elapsedMs.sum, TOL); assertEquals(9999999.0, perBssid.lookupSignal(Event.WIFI_DISABLED, 5805) .elapsedMs.maxValue, TOL); - assertEquals(999.0, perBssid.lookupSignal(Event.FIRST_POLL_AFTER_CONNECTION, 5805) + assertEquals(999.0, perBssid.lookupSignal(Event.FIRST_POLL_AFTER_CONNECTION, 5805) .elapsedMs.minValue, TOL); assertEquals(99999.0, perBssid.lookupSignal(Event.VALIDATION_SUCCESS, 5805) .elapsedMs.sum, TOL); + assertEquals(-88.0, perBssid.lookupSignal(Event.IP_REACHABILITY_LOST, 5805) + .rssi.sum, TOL); assertNull(perBssid.lookupSignal(Event.SIGNAL_POLL, 5805).elapsedMs); } /** + * Firmware roam + */ + @Test + public void testFirmwareRoam() throws Exception { + // Start out disconnected; start connecting + mWifiInfo.setBSSID(android.net.wifi.WifiInfo.DEFAULT_MAC_ADDRESS); + mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID()); + + // First poll has a bad RSSI + millisecondsPass(111); + mWifiInfo.setBSSID(TEST_BSSID_1.toString()); + mWifiInfo.setSupplicantState(SupplicantState.COMPLETED); + mWifiInfo.setFrequency(5805); + mWifiInfo.setRssi(WifiInfo.INVALID_RSSI); + + // A bit later, connection is complete (up through DHCP) + millisecondsPass(222); + mWifiInfo.setRssi(-55); + mWifiScoreCard.noteIpConfiguration(mWifiInfo); + + millisecondsPass(666); + mWifiInfo.setRssi(-77); + // Rssi polls for 99 seconds + for (int i = 0; i < 99; i += 9) { + mWifiScoreCard.noteSignalPoll(mWifiInfo); + secondsPass(9); + } + + // Make sure our simulated time adds up + assertEquals(mMilliSecondsSinceBoot, 99999); + // Validation success, rather late! + mWifiScoreCard.noteValidationSuccess(mWifiInfo); + // Simulate a successful roam + mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, SupplicantState.COMPLETED); + millisecondsPass(1); + mWifiInfo.setBSSID(TEST_BSSID_2.toString()); + mWifiInfo.setRssi(-66); + mWifiInfo.setFrequency(2412); + mWifiInfo.setSupplicantState(SupplicantState.COMPLETED); + mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo); + secondsPass(9); + assertEquals(mMilliSecondsSinceBoot, 109000); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + + // Simulate an unsuccessful roam + secondsPass(1); + mWifiInfo.setRssi(-74); + mWifiScoreCard.noteSignalPoll(mWifiInfo); + secondsPass(1); + mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, SupplicantState.COMPLETED); + mWifiInfo.setBSSID(TEST_BSSID_1.toString()); + mWifiInfo.setFrequency(5805); + mWifiInfo.setSupplicantState(SupplicantState.COMPLETED); + mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo); + secondsPass(3); + mWifiScoreCard.noteIpReachabilityLost(mWifiInfo); + + // Now verify + WifiScoreCard.PerBssid perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_1); + assertEquals(1, perBssid.lookupSignal(Event.IP_CONFIGURATION_SUCCESS, 5805) + .elapsedMs.count); + assertEquals(-77, perBssid.lookupSignal(Event.LAST_POLL_BEFORE_ROAM, 5805) + .rssi.minValue, TOL); + assertEquals(1, perBssid.lookupSignal(Event.ROAM_FAILURE, 5805) + .rssi.count); + + assertEquals(67, perBssid.estimatePercentInternetAvailability()); + + perBssid = mWifiScoreCard.fetchByBssid(TEST_BSSID_2); + assertEquals(-66.0, perBssid.lookupSignal(Event.ROAM_SUCCESS, 2412) + .rssi.sum, TOL); + assertEquals(50, perBssid.estimatePercentInternetAvailability()); + } + + /** * Constructs a protobuf form of AccessPoint example. */ private byte[] makeSerializedAccessPointExample() { |