diff options
| -rwxr-xr-x | wifi/java/android/net/wifi/WifiNetworkScoreCache.java | 102 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java | 272 |
2 files changed, 251 insertions, 123 deletions
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java index c3287481aec9..9dd118bdbc55 100755 --- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java @@ -17,13 +17,19 @@ package android.net.wifi; import android.Manifest.permission; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; +import android.os.Handler; import android.net.INetworkScoreCache; import android.net.NetworkKey; import android.net.ScoredNetwork; import android.util.Log; +import com.android.internal.util.Preconditions; +import com.android.internal.annotations.GuardedBy; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; @@ -43,30 +49,55 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { // 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; + + // See {@link #CacheListener}. + @Nullable + @GuardedBy("mCacheLock") + private CacheListener mListener; + private final Context mContext; + private final Object mCacheLock = new Object(); // 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; + this(context, null /* listener */); + } + + /** + * Instantiates a WifiNetworkScoreCache. + * + * @param context Application context + * @param listener CacheListener for cache updates + */ + public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) { + mContext = context.getApplicationContext(); + mListener = listener; mNetworkCache = new HashMap<String, ScoredNetwork>(); } @Override public final void updateScores(List<ScoredNetwork> networks) { - if (networks == null) { + if (networks == null || networks.isEmpty()) { return; - } - Log.e(TAG, "updateScores list size=" + networks.size()); + } + Log.d(TAG, "updateScores list size=" + networks.size()); - synchronized(mNetworkCache) { - for (ScoredNetwork network : networks) { - String networkKey = buildNetworkKey(network); - if (networkKey == null) continue; - mNetworkCache.put(networkKey, network); - } - } + synchronized(mNetworkCache) { + for (ScoredNetwork network : networks) { + String networkKey = buildNetworkKey(network); + if (networkKey == null) continue; + mNetworkCache.put(networkKey, network); + } + } + + synchronized (mCacheLock) { + if (mListener != null) { + mListener.post(networks); + } + } } @Override public final void clearScores() { @@ -193,4 +224,53 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { } } + /** Registers a CacheListener instance, replacing the previous listener if it existed. */ + public void registerListener(CacheListener listener) { + synchronized (mCacheLock) { + mListener = listener; + } + } + + /** Removes the registered CacheListener. */ + public void unregisterListener() { + synchronized (mCacheLock) { + mListener = null; + } + } + + /** Listener for updates to the cache inside WifiNetworkScoreCache. */ + public abstract static class CacheListener { + + private Handler mHandler; + + /** + * Constructor for CacheListener. + * + * @param handler the Handler on which to invoke the {@link #networkCacheUpdated} method. + * This cannot be null. + */ + public CacheListener(@NonNull Handler handler) { + Preconditions.checkNotNull(handler); + mHandler = handler; + } + + /** Invokes the {@link #networkCacheUpdated(List<ScoredNetwork>)} method on the handler. */ + void post(List<ScoredNetwork> updatedNetworks) { + mHandler.post(new Runnable() { + @Override + public void run() { + networkCacheUpdated(updatedNetworks); + } + }); + } + + /** + * Invoked whenever the cache is updated. + * + * <p>Clearing the cache does not invoke this method. + * + * @param updatedNetworks the networks that were updated + */ + public abstract void networkCacheUpdated(List<ScoredNetwork> updatedNetworks); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java index f8549b9cb715..12d499570ace 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java @@ -17,6 +17,8 @@ package android.net.wifi; import static org.junit.Assert.*; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -24,6 +26,9 @@ import android.net.NetworkKey; import android.net.RssiCurve; import android.net.ScoredNetwork; import android.net.WifiKey; +import android.net.wifi.WifiNetworkScoreCache.CacheListener; +import android.os.Handler; +import android.os.HandlerThread; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -33,124 +38,167 @@ import org.junit.Rule; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + + /** 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)); - } + 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); + + @Mock private Context mockApplicationContext; + @Mock private Context mockContext; // isn't used, can be null + @Mock private RssiCurve mockRssiCurve; + + + private CacheListener mCacheListener; + private CountDownLatch mLatch; + private Handler mHandler; + private List<ScoredNetwork> mUpdatedNetworksCaptor; + 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); + + when(mockContext.getApplicationContext()).thenReturn(mockApplicationContext); + + mValidScoredNetwork = buildScoredNetwork(VALID_KEY, mockRssiCurve); + mScoreCache = new WifiNetworkScoreCache(mockContext); + initializeCacheWithValidScoredNetwork(); + + HandlerThread thread = new HandlerThread("WifiNetworkScoreCacheTest Handler Thread"); + thread.start(); + mHandler = new Handler(thread.getLooper()); + mLatch = new CountDownLatch(1); + mCacheListener = new CacheListener(mHandler) { + @Override + public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) { + mUpdatedNetworksCaptor = updatedNetworks; + mLatch.countDown(); + } + }; + } + + + @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)); + } + + @Test + public void updateScoresShouldInvokeCacheListener_networkCacheUpdated() { + mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener); + initializeCacheWithValidScoredNetwork(); + + try { + mLatch.await(1, TimeUnit.SECONDS); // wait for listener to be executed + } catch (InterruptedException e) { + fail("Interrupted Exception while waiting for listener to be invoked."); + } + assertEquals("One network should be updated", 1, mUpdatedNetworksCaptor.size()); + assertEquals(mValidScoredNetwork, mUpdatedNetworksCaptor.get(0)); + } } |