diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 26ca3d4aa5d7f..6fe581eab86dd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -89,15 +89,15 @@ public class AccessPoint implements Comparable { new ConcurrentHashMap(32); private static final long MAX_SCAN_RESULT_AGE_MS = 15000; - private static final String KEY_NETWORKINFO = "key_networkinfo"; - private static final String KEY_WIFIINFO = "key_wifiinfo"; - private static final String KEY_SCANRESULT = "key_scanresult"; - private static final String KEY_SSID = "key_ssid"; - private static final String KEY_SECURITY = "key_security"; - private static final String KEY_PSKTYPE = "key_psktype"; - private static final String KEY_SCANRESULTCACHE = "key_scanresultcache"; - private static final String KEY_CONFIG = "key_config"; - private static final AtomicInteger sLastId = new AtomicInteger(0); + static final String KEY_NETWORKINFO = "key_networkinfo"; + static final String KEY_WIFIINFO = "key_wifiinfo"; + static final String KEY_SCANRESULT = "key_scanresult"; + static final String KEY_SSID = "key_ssid"; + static final String KEY_SECURITY = "key_security"; + static final String KEY_PSKTYPE = "key_psktype"; + static final String KEY_SCANRESULTCACHE = "key_scanresultcache"; + static final String KEY_CONFIG = "key_config"; + static final AtomicInteger sLastId = new AtomicInteger(0); /** * These values are matched in string arrays -- changes must be kept in sync @@ -114,6 +114,8 @@ public class AccessPoint implements Comparable { public static final int SIGNAL_LEVELS = 4; + static final int UNREACHABLE_RSSI = Integer.MAX_VALUE; + private final Context mContext; private String ssid; @@ -125,7 +127,7 @@ public class AccessPoint implements Comparable { private WifiConfiguration mConfig; - private int mRssi = Integer.MAX_VALUE; + private int mRssi = UNREACHABLE_RSSI; private long mSeen = 0; private WifiInfo mInfo; @@ -214,6 +216,21 @@ public class AccessPoint implements Comparable { this.mRankingScore = that.mRankingScore; } + /** + * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than, + * equal to, or greater than the other AccessPoint. + * + * Sort order rules for AccessPoints: + * 1. Active before inactive + * 2. Reachable before unreachable + * 3. Saved before unsaved + * 4. (Internal only) Network ranking score + * 5. Stronger signal before weaker signal + * 6. SSID alphabetically + * + * Note that AccessPoints with a signal are usually also Reachable, + * and will thus appear before unreachable saved AccessPoints. + */ @Override public int compareTo(@NonNull AccessPoint other) { // Active one goes first. @@ -221,18 +238,16 @@ public class AccessPoint implements Comparable { if (!isActive() && other.isActive()) return 1; // Reachable one goes before unreachable one. - if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1; - if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1; + if (isReachable() && !other.isReachable()) return -1; + if (!isReachable() && other.isReachable()) return 1; // Configured (saved) one goes before unconfigured one. - if (networkId != WifiConfiguration.INVALID_NETWORK_ID - && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1; - if (networkId == WifiConfiguration.INVALID_NETWORK_ID - && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1; + if (isSaved() && !other.isSaved()) return -1; + if (!isSaved() && other.isSaved()) return 1; // Higher scores go before lower scores - if (mRankingScore != other.mRankingScore) { - return (mRankingScore > other.mRankingScore) ? -1 : 1; + if (getRankingScore() != other.getRankingScore()) { + return (getRankingScore() > other.getRankingScore()) ? -1 : 1; } // Sort by signal strength, bucketed by level @@ -242,7 +257,7 @@ public class AccessPoint implements Comparable { return difference; } // Sort by ssid. - return ssid.compareToIgnoreCase(other.ssid); + return getSsidStr().compareToIgnoreCase(other.getSsidStr()); } @Override @@ -355,7 +370,7 @@ public class AccessPoint implements Comparable { } public int getLevel() { - if (mRssi == Integer.MAX_VALUE) { + if (!isReachable()) { return -1; } return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS); @@ -531,7 +546,7 @@ public class AccessPoint implements Comparable { } } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); - } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range + } else if (!isReachable()) { // Wifi out of range summary.append(mContext.getString(R.string.wifi_not_in_range)); } else { // In range, not disabled. if (config != null) { // Is saved network @@ -874,6 +889,11 @@ public class AccessPoint implements Comparable { return mBadge; } + /** Return true if the current RSSI is reachable, and false otherwise. */ + boolean isReachable() { + return mRssi != UNREACHABLE_RSSI; + } + public static String getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider) { if (state == DetailedState.CONNECTED && ssid == null) { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 6481f4d6531d0..ec0190cfa538c 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -15,9 +15,8 @@ */ package com.android.settingslib.wifi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.net.ConnectivityManager; @@ -39,6 +38,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -59,12 +59,12 @@ public class AccessPointTest { final AccessPoint ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle); final CharSequence ssid = ap.getSsid(); - assertTrue(ssid instanceof SpannableString); + assertThat(ssid instanceof SpannableString).isTrue(); TtsSpan[] spans = ((SpannableString) ssid).getSpans(0, TEST_SSID.length(), TtsSpan.class); - assertEquals(1, spans.length); - assertEquals(TtsSpan.TYPE_TELEPHONE, spans[0].getType()); + assertThat(spans.length).isEqualTo(1); + assertThat(spans[0].getType()).isEqualTo(TtsSpan.TYPE_TELEPHONE); } @Test @@ -80,11 +80,11 @@ public class AccessPointTest { originalAccessPoint.update(configuration, wifiInfo, networkInfo); AccessPoint copy = new AccessPoint(mContext, originalAccessPoint); - assertEquals(originalAccessPoint.getSsid().toString(), copy.getSsid().toString()); - assertEquals(originalAccessPoint.getBssid(), copy.getBssid()); - assertSame(originalAccessPoint.getConfig(), copy.getConfig()); - assertEquals(originalAccessPoint.getSecurity(), copy.getSecurity()); - assertTrue(originalAccessPoint.compareTo(copy) == 0); + assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString()); + assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid()); + assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig()); + assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity()); + assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue(); } @Test @@ -101,11 +101,93 @@ public class AccessPointTest { bundle.putParcelableArrayList("key_scanresultcache", scanResults); AccessPoint original = new AccessPoint(mContext, bundle); - assertEquals(4, original.getRssi()); + assertThat(original.getRssi()).isEqualTo(4); AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration()); - assertEquals(Integer.MIN_VALUE, copy.getRssi()); + assertThat(copy.getRssi()).isEqualTo(Integer.MIN_VALUE); copy.copyFrom(original); - assertEquals(original.getRssi(), copy.getRssi()); + assertThat(original.getRssi()).isEqualTo(copy.getRssi()); + } + + @Test + public void testCompareTo_GivesActiveBeforeInactive() { + AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); + AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build(); + + assertSortingWorks(activeAp, inactiveAp); + } + + @Test + public void testCompareTo_GivesReachableBeforeUnreachable() { + AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build(); + AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build(); + + assertSortingWorks(nearAp, farAp); + } + + @Test + public void testCompareTo_GivesSavedBeforeUnsaved() { + AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build(); + AccessPoint notSavedAp = new TestAccessPointBuilder(mContext).setSaved(false).build(); + + assertSortingWorks(savedAp, notSavedAp); + } + + //TODO: add tests for mRankingScore sort order if ranking is exposed + + @Test + public void testCompareTo_GivesHighLevelBeforeLowLevel() { + final int highLevel = AccessPoint.SIGNAL_LEVELS - 1; + final int lowLevel = 1; + assertThat(highLevel).isGreaterThan(lowLevel); + + AccessPoint strongAp = new TestAccessPointBuilder(mContext).setLevel(highLevel).build(); + AccessPoint weakAp = new TestAccessPointBuilder(mContext).setLevel(lowLevel).build(); + + assertSortingWorks(strongAp, weakAp); + } + + @Test + public void testCompareTo_GivesSsidAlphabetically() { + + final String firstName = "AAAAAA"; + final String secondName = "zzzzzz"; + + AccessPoint firstAp = new TestAccessPointBuilder(mContext).setSsid(firstName).build(); + AccessPoint secondAp = new TestAccessPointBuilder(mContext).setSsid(secondName).build(); + + assertThat(firstAp.getSsidStr().compareToIgnoreCase(secondAp.getSsidStr()) < 0).isTrue(); + assertSortingWorks(firstAp, secondAp); + } + + @Test + public void testCompareTo_AllSortingRulesCombined() { + + AccessPoint active = new TestAccessPointBuilder(mContext).setActive(true).build(); + AccessPoint reachableAndMinLevel = new TestAccessPointBuilder(mContext) + .setReachable(true).build(); + AccessPoint saved = new TestAccessPointBuilder(mContext).setSaved(true).build(); + AccessPoint highLevelAndReachable = new TestAccessPointBuilder(mContext) + .setLevel(AccessPoint.SIGNAL_LEVELS - 1).build(); + AccessPoint firstName = new TestAccessPointBuilder(mContext).setSsid("a").build(); + AccessPoint lastname = new TestAccessPointBuilder(mContext).setSsid("z").build(); + + ArrayList points = new ArrayList(); + points.add(lastname); + points.add(firstName); + points.add(highLevelAndReachable); + points.add(saved); + points.add(reachableAndMinLevel); + points.add(active); + + Collections.sort(points); + assertThat(points.indexOf(active)).isLessThan(points.indexOf(reachableAndMinLevel)); + assertThat(points.indexOf(reachableAndMinLevel)).isLessThan(points.indexOf(saved)); + // note: the saved AP will not appear before highLevelAndReachable, + // because all APs with a signal level are reachable, + // and isReachable() takes higher sorting precedence than isSaved(). + assertThat(points.indexOf(saved)).isLessThan(points.indexOf(firstName)); + assertThat(points.indexOf(highLevelAndReachable)).isLessThan(points.indexOf(firstName)); + assertThat(points.indexOf(firstName)).isLessThan(points.indexOf(lastname)); } private WifiConfiguration createWifiConfiguration() { @@ -115,4 +197,85 @@ public class AccessPointTest { configuration.networkId = 123; return configuration; } + + /** + * Assert that the first AccessPoint appears after the second AccessPoint + * once sorting has been completed. + */ + private void assertSortingWorks(AccessPoint first, AccessPoint second) { + + ArrayList points = new ArrayList(); + + // add in reverse order so we can tell that sorting actually changed something + points.add(second); + points.add(first); + Collections.sort(points); + assertWithMessage( + String.format("After sorting: second AccessPoint should have higher array index " + + "than the first, but found indicies second '%s' and first '%s'.", + points.indexOf(second), points.indexOf(first))) + .that(points.indexOf(second)).isGreaterThan(points.indexOf(first)); + } + + @Test + public void testBuilder_setActive() { + AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); + assertThat(activeAp.isActive()).isTrue(); + + AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build(); + assertThat(inactiveAp.isActive()).isFalse(); + } + + @Test + public void testBuilder_setReachable() { + AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build(); + assertThat(nearAp.isReachable()).isTrue(); + + AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build(); + assertThat(farAp.isReachable()).isFalse(); + } + + @Test + public void testBuilder_setSaved() { + AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build(); + assertThat(savedAp.isSaved()).isTrue(); + + AccessPoint newAp = new TestAccessPointBuilder(mContext).setSaved(false).build(); + assertThat(newAp.isSaved()).isFalse(); + } + + @Test + public void testBuilder_setLevel() { + AccessPoint testAp; + + for (int i = 0; i < AccessPoint.SIGNAL_LEVELS; i++) { + testAp = new TestAccessPointBuilder(mContext).setLevel(i).build(); + assertThat(testAp.getLevel()).isEqualTo(i); + } + + // numbers larger than the max level should be set to max + testAp = new TestAccessPointBuilder(mContext).setLevel(AccessPoint.SIGNAL_LEVELS).build(); + assertThat(testAp.getLevel()).isEqualTo(AccessPoint.SIGNAL_LEVELS - 1); + + // numbers less than 0 should give level 0 + testAp = new TestAccessPointBuilder(mContext).setLevel(-100).build(); + assertThat(testAp.getLevel()).isEqualTo(0); + } + + @Test + public void testBuilder_settingReachableAfterLevelDoesNotAffectLevel() { + int level = 1; + assertThat(level).isLessThan(AccessPoint.SIGNAL_LEVELS - 1); + + AccessPoint testAp = + new TestAccessPointBuilder(mContext).setLevel(level).setReachable(true).build(); + assertThat(testAp.getLevel()).isEqualTo(level); + } + + @Test + public void testBuilder_setSsid() { + String name = "AmazingSsid!"; + AccessPoint namedAp = new TestAccessPointBuilder(mContext).setSsid(name).build(); + assertThat(namedAp.getSsidStr()).isEqualTo(name); + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java new file mode 100644 index 0000000000000..665c439c3d6d3 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 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 com.android.settingslib.wifi; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiConfiguration; +import android.os.Bundle; + +/** +* Build and return a valid AccessPoint. +* +* Only intended for testing the AccessPoint class; +* AccessPoints were designed to only be populated +* by the mechanisms of scan results and wifi configurations. +*/ +public class TestAccessPointBuilder { + // match the private values in WifiManager + private static final int MIN_RSSI = -100; + private static final int MAX_RSSI = -55; + + // set some sensible defaults + private int mRssi = AccessPoint.UNREACHABLE_RSSI; + private int networkId = WifiConfiguration.INVALID_NETWORK_ID; + private String ssid = "TestSsid"; + private NetworkInfo mNetworkInfo = null; + + Context mContext; + + public TestAccessPointBuilder(Context context) { + mContext = context; + } + + public AccessPoint build() { + Bundle bundle = new Bundle(); + + WifiConfiguration wifiConig = new WifiConfiguration(); + wifiConig.networkId = networkId; + + bundle.putString(AccessPoint.KEY_SSID, ssid); + bundle.putParcelable(AccessPoint.KEY_CONFIG, wifiConig); + bundle.putParcelable(AccessPoint.KEY_NETWORKINFO, mNetworkInfo); + AccessPoint ap = new AccessPoint(mContext, bundle); + ap.setRssi(mRssi); + return ap; + } + + public TestAccessPointBuilder setActive(boolean active) { + if (active) { + mNetworkInfo = new NetworkInfo( + ConnectivityManager.TYPE_DUMMY, + ConnectivityManager.TYPE_DUMMY, + "TestNetwork", + "TestNetwork"); + } else { + mNetworkInfo = null; + } + return this; + } + + /** + * Set the signal level. + * Side effect: if this AccessPoint was previously unreachable, + * setting the level will also make it reachable. + */ + public TestAccessPointBuilder setLevel(int level) { + int outputRange = AccessPoint.SIGNAL_LEVELS - 1; + + if (level > outputRange) { + level = outputRange; + } else if (level < 0) { + level = 0; + } + + int inputRange = MAX_RSSI - MIN_RSSI; + + // calculate the rssi required to get the level we want. + // this is a rearrangement of the formula from WifiManager.calculateSignalLevel() + mRssi = (int)((float)(level * inputRange) / (float)outputRange) + MIN_RSSI; + return this; + } + + /** + * Set whether the AccessPoint is reachable. + * Side effect: if the signal level was not previously set, + * making an AccessPoint reachable will set the signal to the minimum level. + */ + public TestAccessPointBuilder setReachable(boolean reachable) { + if (reachable) { + // only override the mRssi if it hasn't been set yet + if (mRssi == AccessPoint.UNREACHABLE_RSSI) { + mRssi = MIN_RSSI; + } + } else { + mRssi = AccessPoint.UNREACHABLE_RSSI; + } + return this; + } + + public TestAccessPointBuilder setSaved(boolean saved){ + if (saved) { + networkId = 1; + } else { + networkId = WifiConfiguration.INVALID_NETWORK_ID; + } + return this; + } + + public TestAccessPointBuilder setSsid(String newSsid) { + ssid = newSsid; + return this; + } +}