diff options
| -rwxr-xr-x | wifi/java/android/net/wifi/WifiNetworkScoreCache.java | 196 | ||||
| -rw-r--r-- | wifi/tests/Android.mk | 1 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java | 156 |
3 files changed, 353 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java new file mode 100755 index 000000000000..c3287481aec9 --- /dev/null +++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014 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; + +import android.Manifest.permission; +import android.annotation.SystemApi; +import android.content.Context; +import android.net.INetworkScoreCache; +import android.net.NetworkKey; +import android.net.ScoredNetwork; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * {@link INetworkScoreCache} implementation for Wifi Networks. + * + * @hide + */ +public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { + private static final String TAG = "WifiNetworkScoreCache"; + private static final boolean DBG = false; + + // A Network scorer returns a score in the range [-128, +127] + // We treat the lowest possible score as though there were no score, effectively allowing the + // scorer to provide an RSSI threshold below which a network should not be used. + public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE; + private final Context mContext; + + // The key is of the form "<ssid>"<bssid> + // TODO: What about SSIDs that can't be encoded as UTF-8? + private final Map<String, ScoredNetwork> mNetworkCache; + + public WifiNetworkScoreCache(Context context) { + mContext = context; + mNetworkCache = new HashMap<String, ScoredNetwork>(); + } + + @Override public final void updateScores(List<ScoredNetwork> networks) { + if (networks == null) { + return; + } + Log.e(TAG, "updateScores list size=" + networks.size()); + + synchronized(mNetworkCache) { + for (ScoredNetwork network : networks) { + String networkKey = buildNetworkKey(network); + if (networkKey == null) continue; + mNetworkCache.put(networkKey, network); + } + } + } + + @Override public final void clearScores() { + synchronized (mNetworkCache) { + mNetworkCache.clear(); + } + } + + /** + * Returns whether there is any score info for the given ScanResult. + * + * This includes null-score info, so it should only be used when determining whether to request + * scores from the network scorer. + */ + public boolean isScoredNetwork(ScanResult result) { + return getScoredNetwork(result) != null; + } + + /** + * Returns whether there is a non-null score curve for the given ScanResult. + * + * A null score curve has special meaning - we should never connect to an ephemeral network if + * the score curve is null. + */ + public boolean hasScoreCurve(ScanResult result) { + ScoredNetwork network = getScoredNetwork(result); + return network != null && network.rssiCurve != null; + } + + public int getNetworkScore(ScanResult result) { + + int score = INVALID_NETWORK_SCORE; + + ScoredNetwork network = getScoredNetwork(result); + if (network != null && network.rssiCurve != null) { + score = network.rssiCurve.lookupScore(result.level); + if (DBG) { + Log.e(TAG, "getNetworkScore found scored network " + network.networkKey + + " score " + Integer.toString(score) + + " RSSI " + result.level); + } + } + return score; + } + + /** + * Returns the ScoredNetwork metered hint for a given ScanResult. + * + * If there is no ScoredNetwork associated with the ScanResult then false will be returned. + */ + public boolean getMeteredHint(ScanResult result) { + ScoredNetwork network = getScoredNetwork(result); + return network != null && network.meteredHint; + } + + public int getNetworkScore(ScanResult result, boolean isActiveNetwork) { + + int score = INVALID_NETWORK_SCORE; + + ScoredNetwork network = getScoredNetwork(result); + if (network != null && network.rssiCurve != null) { + score = network.rssiCurve.lookupScore(result.level, isActiveNetwork); + if (DBG) { + Log.e(TAG, "getNetworkScore found scored network " + network.networkKey + + " score " + Integer.toString(score) + + " RSSI " + result.level + + " isActiveNetwork " + isActiveNetwork); + } + } + return score; + } + + private ScoredNetwork getScoredNetwork(ScanResult result) { + String key = buildNetworkKey(result); + if (key == null) return null; + + //find it + synchronized(mNetworkCache) { + ScoredNetwork network = mNetworkCache.get(key); + return network; + } + } + + private String buildNetworkKey(ScoredNetwork network) { + if (network == null || network.networkKey == null) return null; + if (network.networkKey.wifiKey == null) return null; + if (network.networkKey.type == NetworkKey.TYPE_WIFI) { + String key = network.networkKey.wifiKey.ssid; + if (key == null) return null; + if (network.networkKey.wifiKey.bssid != null) { + key = key + network.networkKey.wifiKey.bssid; + } + return key; + } + return null; + } + + private String buildNetworkKey(ScanResult result) { + if (result == null || result.SSID == null) { + return null; + } + StringBuilder key = new StringBuilder("\""); + key.append(result.SSID); + key.append("\""); + if (result.BSSID != null) { + key.append(result.BSSID); + } + return key.toString(); + } + + @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); + writer.println("WifiNetworkScoreCache"); + writer.println(" All score curves:"); + for (Map.Entry<String, ScoredNetwork> entry : mNetworkCache.entrySet()) { + ScoredNetwork scoredNetwork = entry.getValue(); + writer.println(" " + entry.getKey() + ": " + scoredNetwork.rssiCurve + + ", meteredHint=" + scoredNetwork.meteredHint); + } + writer.println(" Current network scores:"); + WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + for (ScanResult scanResult : wifiManager.getScanResults()) { + writer.println(" " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); + } + } + +} diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk index 5850feee004a..eac49d20b88d 100644 --- a/wifi/tests/Android.mk +++ b/wifi/tests/Android.mk @@ -50,6 +50,7 @@ LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ + guava \ mockito-target-minus-junit4 \ frameworks-base-testutils \ diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java new file mode 100644 index 000000000000..f8549b9cb715 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 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; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.NetworkKey; +import android.net.RssiCurve; +import android.net.ScoredNetwork; +import android.net.WifiKey; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Unit tests for {@link WifiNetworkScoreCache}. */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WifiNetworkScoreCacheTest { + + @Mock public Context mockContext; // isn't used, can be null + @Mock private RssiCurve mockRssiCurve; + + public static final String SSID = "ssid"; + public static final String FORMATTED_SSID = "\"" + SSID + "\""; + public static final String BSSID = "AA:AA:AA:AA:AA:AA"; + + public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID); + + public static final ScanResult VALID_SCAN_RESULT = buildScanResult(SSID, BSSID); + + private ScoredNetwork mValidScoredNetwork; + private WifiNetworkScoreCache mScoreCache = + new WifiNetworkScoreCache(mockContext); + + private static ScanResult buildScanResult(String ssid, String bssid) { + return new ScanResult( + WifiSsid.createFromAsciiEncoded(ssid), + bssid, + "" /* caps */, + 0 /* level */, + 0 /* frequency */, + 0 /* tsf */, + 0 /* distCm */, + 0 /* distSdCm*/); + } + + private static ScoredNetwork buildScoredNetwork(WifiKey key, RssiCurve curve) { + return new ScoredNetwork(new NetworkKey(key), curve); + } + + // Called from setup + private void initializeCacheWithValidScoredNetwork() { + mScoreCache.updateScores(ImmutableList.of(mValidScoredNetwork)); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve); + mScoreCache = new WifiNetworkScoreCache(mockContext); + initializeCacheWithValidScoredNetwork(); + } + + + @Test + public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() { + assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)); + } + + @Test + public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() { + mScoreCache.clearScores(); + assertFalse(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)); + } + + @Test + public void updateScoresShouldAddNewNetwork() { + WifiKey key2 = new WifiKey("\"ssid2\"", BSSID); + ScoredNetwork network2 = buildScoredNetwork(key2, mockRssiCurve); + ScanResult result2 = buildScanResult("ssid2", BSSID); + + mScoreCache.updateScores(ImmutableList.of(network2)); + + assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)); + assertTrue(mScoreCache.isScoredNetwork(result2)); + } + + @Test + public void hasScoreCurveShouldReturnTrue() { + assertTrue(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)); + } + + @Test + public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() { + ScanResult unscored = buildScanResult("fake", BSSID); + assertFalse(mScoreCache.hasScoreCurve(unscored)); + } + + @Test + public void hasScoreCurveShouldReturnFalseWhenScoredNetworkHasNoCurve() { + ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */); + mScoreCache.updateScores(ImmutableList.of(noCurve)); + + assertFalse(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)); + } + + @Test + public void getNetworkScoreShouldReturnScore() { + final byte score = 50; + final int rssi = -70; + ScanResult result = new ScanResult(VALID_SCAN_RESULT); + result.level = rssi; + + when(mockRssiCurve.lookupScore(rssi)).thenReturn(score); + + assertEquals(score, mScoreCache.getNetworkScore(result)); + } + + @Test + public void getMeteredHintShouldReturnFalse() { + assertFalse(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)); + } + + @Test + public void getMeteredHintShouldReturnTrue() { + ScoredNetwork network = + new ScoredNetwork(new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */); + mScoreCache.updateScores(ImmutableList.of(network)); + + assertTrue(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)); + } +} |